搜尋此網誌

顯示具有 Async Task 標籤的文章。 顯示所有文章
顯示具有 Async Task 標籤的文章。 顯示所有文章

2017年11月12日 星期日

【Android】This AsyncTask class should be static or leaks might occur

How to use a static inner AsyncTask class

ref:https://stackoverflow.com/a/46166223

To prevent leaks, you can make the the inner class static. The problem with that, though, is that you no longer have access to the Activity's UI views or member variables. You can pass in a reference to the Context but then you run the same risk of a memory leak. (Android can't garbage collect the Activity after it closes if the AsyncTask class has a strong reference to it.) The solution is to make a weak reference to the Activity (or whatever Context you need).
public class MyActivity extends AppCompatActivity {

    int mSomeMemberVariable = 123;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor 
        new MyTask(this).execute();
    }

    private static class MyTask extends AsyncTask<Void, Void, String> {

        private WeakReference<MyActivity> activityReference;

        // only retain a weak reference to the activity 
        MyTask(MyActivity context) {
            activityReference = new WeakReference<>(context);
        }

        @Override
        protected String doInBackground(Void... params) {

            // do some long running task...

            return "task finished";
        }

        @Override
        protected void onPostExecute(String result) {

            // get a reference to the activity if it is still there
            MyActivity activity = activityReference.get();
            if (activity == null) return;

            // modify the activity's UI
            TextView textView = activity.findViewById(R.id.textview);
            textView.setText(result);

            // access Activity member variables
            activity.mSomeMemberVariable = 321;
        }
    }
}

Notes

  • As far as I know, this type of memory leak danger has always been true, but I only started seeing the warning in Android Studio 3.0. A lot of the main AsyncTask tutorials out there still don't deal with it (see hereherehere, and here).
  • You would also follow a similar procedure if your AsyncTask were a top-level class. A static inner class is basically the same as a top-level class in Java.
  • If you don't need the Activity itself but still want the Context (for example, to display a Toast), you can pass in a reference to the app context. In this case the AsyncTask constructor would look like this:
    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
  • There are some arguments out there for ignoring this warning and just using the non-static class. After all, the AsyncTask is intended to be very short lived (a couple seconds at the longest), and it will release its reference to the Activity when it finishes anyway. See this and this.
  • Excellent article: How to Leak a Context: Handlers & Inner Classes

2015年8月30日 星期日

【Android】Volley - 好用官方的httpclient library

Volley是Gogle在Google I/O 2013所發表的網路框架,主要是為了加強Android網路應用的效能。推出Volley的原因是:
  • 目前廣泛使用的HttpURLConnectionHttpClient有已知的問題和bug是不易修復的,甚至HttpClient可能不再被維護
  • 目前在Android上抓取網路資料的標準作法,是需要透過執行緒,再把結果送回主程序,過程稍嫌複雜
可說是集中非同步的網路通訊優點,同時提供了字串、JSON、圖片三種請求方式。內部再把Http和執行緒封裝起來。還提供Cache,使得在performance上改善不少。
Google建議使用Volley來存取資料量不大且頻繁的網路通訊,Volley在大量資料的傳輸上,效能很差。

目前沒有Google官方提供的Volley封裝檔,建議如下步驟:

String、Json、Image三個的寫法差不多,都是先從URL連結啟動請求之後,將請求加到Queue去處理(請注意,這是非同步的),若成功則在onResponse事件中取得回傳值,若失敗則會在onErrorResponse得到錯誤訊息。

for Gradle

compile 'com.mcxiaoke.volley:library:1.0.19'

Premission (AndroidManifest.xml)

別忘了,Volley 是需要是用網路的,所以記得加上這一行。


<uses-permission android:name="android.permission.INTERNET" />

Example

RequestQueue mQueue = Volley.newRequestQueue(context);

  • StringRequest 
String url ="http://www.google.com";
StringRequest stringRequest = new StringRequest(url,new Response.Listener<String>() {
  @Override
  public void onResponse(String response) {
   Log.d("TAG", response);
  }
 }, new Response.ErrorListener() {
  @Override
  public void onErrorResponse(VolleyError error) {
   Log.e("TAG", error.getMessage(), error);
  }
 });
 
mQueue.add(stringRequest);
  • JsonRequest
String url ="http://my-json-feed";
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, "UTF-8",
  new Response.Listener<JSONObject>() {
   @Override
   public void onResponse(JSONObject response) {
    Log.d("TAG", response.toString());
   }
  }, new Response.ErrorListener() {
   @Override
   public void onErrorResponse(VolleyError error) {
    Log.e("TAG", error.getMessage(), error);
   }
  });
 
mQueue.add(jsonObjectRequest);
  • Image Request
String url="http://i.imgur.com/rwTYZzF.png";
ImageRequest imageRequest = new ImageRequest(
  url, new Response.Listener<Bitmap>() {
  @Override
  public void onResponse(Bitmap response) {
   imageView.setImageBitmap(response);
  }
 }, 0, 0, null, new Response.ErrorListener() {
   @Override
   public void onErrorResponse(VolleyError error) {
    imageView.setImageResource(R.drawable.default_image);
   }
  });
mQueue.add(imageRequest);

可稍微改寫一下JsonRequest使得資料更好取得
String url = "http://my-json-feed";
        // 取得資料並編碼
        JsonArrayPostRequest jRequest = new JsonArrayPostRequest(url,
                new Response.Listener<JSONArray>() {

                    public void onResponse(JSONArray response) {

                        //... easy to do something

                    }
                }, new Response.ErrorListener() {

            public void onErrorResponse(VolleyError error) {
                VolleyLog.d(TAG, "Error: " + error.getMessage());

            }
        });
        // 設定執行為最高優先權
  jRequest.setPriority(Request.Priority.HIGH);
        
  mQueue.add(jRequest);

ref:
http://www.androidhive.info/2014/05/android-working-with-volley-library-1/
http://code.tutsplus.com/tutorials/an-introduction-to-volley--cms-23800
https://github.com/mcxiaoke/android-volley
http://blog.csdn.net/guolin_blog/article/details/17482095

2015年8月22日 星期六

【Android】AsyncTask cant be executed twice

問題:
Cannot execute task: the task has already been executed (a task can be executed only once)
http://stackoverflow.com/questions/19345118/async-task-cant-be-executed-twice

解答:
As the exception itself explains, you cannot execute an AsyncTask more than once, unless you create a new instance of it and call .execute.
For example:
async = new AsyncTask();
async.execute();
*in order to execute more than once, you need to re-create the instance (using new) the number of times you want to execute it.
但有人說沒作用

問題:
Android restart AsyncTask
http://stackoverflow.com/questions/11586704/android-restart-asynctask

解答:
改成使用Thread 和 HandleMessage實作
Thank you for all your comments they helped a lot. I decided to do away with the AsyncTask. I ended using a normal runnable Thread and using Handlers to post messages back to the UI thread. here is the code:
        // Start thread here only for IsSocketConnected
        new Thread(new Runnable() {

            public void run() {

                //Add your code here..
                IsSocketConnected();

            }
        }).start();


// handler that deals with updating UI
public Handler myUIHandler = new Handler()
{
    @Override
    public void handleMessage(Message msg)
    {
        if (msg.what == Bluetooth.STATE_CONNECTED)
        {
            //Update UI here...
            Log.d(TAG, "Connected");




            // Discover available devices settings and create buttons
            CreateButtons(btnList);

        } else if(msg.what == Bluetooth.STATE_NONE) {

            Log.d(TAG, "NOT Connected");
        }
}
// in the IsSocketConnected() I call this 
Message theMessage = myUIHandler.obtainMessage(Bluetooth.STATE_CONNECTED);
myUIHandler.sendMessage(theMessage);//Sends the message to the UI handler.
This is working so far. Thank you again. Hope this helps someone