开发者

Use of Handler Android

开发者 https://www.devze.com 2023-02-15 16:35 出处:网络
Which is the better way to use a handler. Any advantages. All examples I have come across seem to give the inline ver开发者_StackOverflowsion.

Which is the better way to use a handler. Any advantages. All examples I have come across seem to give the inline ver开发者_StackOverflowsion.

Using implements Handler.Callback in the class and implementing interface method.

or

Using inline code version

private Handler mHandler = new Handler(){ ....};


The common term or these inline class definitions is Anonymous Classes.

You can read more about the discussion on these in Java/Android: anonymous local classes vs named classes

Essentially the main differences are readbility, speed of coding, re-use and scope.

From a resource point of view the anonymous class creation may cause an overhead in the garbage collector as discussed in Avoid Creating Unnecessary Objects. I am not certain on the exact details of anonymous class creation, however, it is logical that implementing the interface on the class is more efficient.

@WilliamTMallard has provided an example of what NOT to do. In his example, a long and syntacticly complex handler should be implementented on the class rather than anonymous handler because it is harder to read and edit when defined inline.


http://developer.android.com/reference/android/os/Handler.html

package : android.os
public class
Handler
extends Object

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler:

  1. to schedule messages and runnables to be executed as some point in the future; and
  2. to enqueue an action to be performed on a different thread than your own.

Exmaple 1

use handler in app splash page.

if (!isFirstIn) {
    mHandler.sendEmptyMessageDelayed(GO_HOME, SPLASH_DELAY_MILLIS);
} else {
    mHandler.sendEmptyMessageDelayed(GO_GUIDE, SPLASH_DELAY_MILLIS);
} 


/**************************************************************************************
*1. Handler
*/
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        if(isAuto){
            switch (msg.what) {
            case GO_HOME:
                goHome();
                break;
            case GO_GUIDE:
                goGuide();
                break;
            }
        }
        super.handleMessage(msg);
    }
}; 
private void goHome() {
    Intent intent = new Intent(SplashActivity.this, MainAct.class);
    SplashActivity.this.startActivity(intent);
    SplashActivity.this.finish();
} 

private void goGuide() {
    Intent intent = new Intent(SplashActivity.this, GuideActivity.class);
    SplashActivity.this.startActivity(intent);
    SplashActivity.this.finish();
} 

Example 2

use Handler request network in child thread if the request work may takes time.

new Thread(new Runnable(){
    @Override
    public void run() {
        String versionPath = Parameters.getCheckVersionPath();
        String result = RequestHelper.doGet(versionPath, null);
        Message msg = new Message();
        Bundle data = new Bundle();
        data.putString("result",result);
        msg.setData(data);
        handler1.sendMessage(msg);
    }
}).start();

handler1 = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        String result = msg.getData().getString("result");
        JSONObject obj;
        try {
            obj = new JSONObject(result);
            Map<String, String> versionInfo = Helper.getSoftwareVersion(obj);
            if (versionInfo != null) {
                newVersion = versionInfo.get("version");
                updateUrl = versionInfo.get("url");
            }
        } catch (JSONException e) {
            Log.w("net work error!", e);
        }
    }

}; 

Example 3

use Handler and Timer to update progress bar.

logobar = (ImageView) findViewById(R.id.splash_bar);//progress bar.
logobarClipe = (ClipDrawable) logobar.getBackground();

timer = new Timer();
timer.schedule(new TimerTask() {
    public void run() {
        updateLogoBarHandler.sendEmptyMessage(0);
}}, 0, rate);


/**************************************************************************************
*2. Handler
*/
//update progress bar.
private Handler updateLogoBarHandler = new Handler() {
    public void handleMessage(Message msg) {
        if(logobarClipe.getLevel() < 10000){
            //1.update image.
            logobarClipe.setLevel(logobarClipe.getLevel() + rate*2);  

            //2.update text.
            float percent = logobarClipe.getLevel() /100;
            String percentTxtVerbose = String.valueOf(percent);
            String percentTxt = percentTxtVerbose.substring(0, percentTxtVerbose.indexOf('.')) + "%";
            bartxt.setText(percentTxt);

        }else{
            timer.cancel();
        }  
        super.handleMessage(msg);
    }
}; 


This really isn't an answer to the above question because I don't know what "the best way" is, and it likely depends on what you're doing. However, I'll explain what I'm doing and why.

I'm writing an app that serves as a remote controller. There are several activities that will interact with the controlled device, and different things need to happen based on the result of the command and the activity it came from. Two things I didn't like about handlers are A) that they end up being a sort of "kitchen sink" construct, implementing functionality from different sources, and B) that they separated an action (the send of the command in my case) from the processing of the result of that action. However, using an anonymous (right term? I'm such a noob.) handler as a parameter allows me to keep the logic together. Here's the pseudocode for my approach:

    command = "Wake up!";

    mDeviceInterface.write(command, new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
            case DeviceInterface.MESSAGE_TIMEOUT: // Process the timeout.
                announce("Device not responding.");
                break;
            case DeviceInterface.MESSAGE_READ: // Process the response.
                byte[] readBuf = (byte[]) msg.obj;
                if (readBuf[0] == 0x05) {
                    // Success, update device status.
                } else {  
                    announce("Error!");
                    break;  
                }
            }
        }
    });

(Always remember, this is probably worth exactly what you've paid for it. ;) )


There is a danger in using anonymous classes in Android. As described in this blog post -

In Java, non-static inner and anonymous classes hold an implicit reference to their outer class.

And here comes an opportunity for a leak.

So, the short answer would be: implement the interface methods or use static inner classes (which don't hold an outer class reference).

For instance, a leak-safe Handler could look like this:

private static class ChangeTextHandler extends Handler {
    private final WeakReference activity;

    public ChangeTextHandler(MainActivity activity) {
        this.activity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        MainActivity activity = this.activity.get();
        if (activity == null) {
            Log.e(TAG, "Activity is null ChangeTextHandler.handleMessage()!");
            return;
        }

        final String text = (String) msg.getData().get(BUNDLE_KEY);
        if (!TextUtils.isEmpty(text)) {
            switch (msg.what) {
                // do something
            }
        }
    }
}

I made a blog post around usage of Handlers, so might be worth checking as well :)

0

精彩评论

暂无评论...
验证码 换一张
取 消