fred-ye / summary

my blog
43 stars 9 forks source link

[Android] 那些零碎的知识点 #49

Open fred-ye opened 9 years ago

fred-ye commented 9 years ago

Android 中那些零碎的知识点

1 IntentService

IntentServiceService的子类,用来处理同步的请求。在IntentService中会开一个工作线程来处理请求。我们可以通过类似于启动Service的方来来启动一个IntentService, IntentService每创建一次,就会新开启一个工作线程来执行请求。执行完一个intent请求对应的工作后,如果没有新的请求到达,则自动停止IntentService, 否则会执行下一个请求。所有需要在后台运行的逻辑我们需要写在onHandleIntent方法中。感觉网上有些博客没说得太清楚,这里自己写个Demo, 加深一下印象

来看这么一段代码:

MyIntentService

public class MyIntentService extends IntentService {
    private final static String TAG = "MyIntentService";
    public MyIntentService() {
        super(TAG);

    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate pid:" + android.os.Process.myPid() + "    thread id:" + Thread.currentThread().getId());

    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, "onHandleIntent pid:" + android.os.Process.myPid() + "    thread id:" + Thread.currentThread().getId());
    }
}

调用者

假设这个代码是当用户点击一个按钮后执行的。

Log.i(TAG, "caller pid:" + android.os.Process.myPid() + "    thread id:" + Thread.currentThread().getId());
startService(new Intent(AActivity.this, MyIntentService.class));
try {
    Thread.sleep(3000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
startService(new Intent(AActivity.this, MyIntentService.class));

运行后发现

06-11 09:36:39.113    1680-1680/? I/AActivity﹕ caller pid:1680    thread id:1
06-11 09:36:42.118    1680-1680/? I/MyIntentService﹕ onCreate pid:1680    thread id:1
06-11 09:36:42.119    1680-1703/? I/MyIntentService﹕ onHandleIntent pid:1680    thread id:163
06-11 09:36:42.119    1680-1703/? I/MyIntentService﹕ onHandleIntent pid:1680    thread id:163

在这个例子通过观察线程id,发现虽然启动了两次这个IntentService,但IntentService只创建了一次。

如果我们将调用的代码改成

Log.i(TAG, "caller pid:" + android.os.Process.myPid() + "    thread id:" + Thread.currentThread().getId());
startService(new Intent(AActivity.this, MyIntentService.class));

同样假设这个代码是当用户点击一个按钮时执行。若用户连续点击这个按钮两次,运行结果发现

06-11 09:45:35.454    1784-1784/? I/AActivity﹕ caller pid:1784    thread id:1
06-11 09:45:35.469    1784-1784/? I/MyIntentService﹕ onCreate pid:1784    thread id:1
06-11 09:45:35.469    1784-1807/? I/MyIntentService﹕ onHandleIntent pid:1784    thread id:169
06-11 09:45:35.773    1784-1784/? I/AActivity﹕ caller pid:1784    thread id:1
06-11 09:45:35.777    1784-1784/? I/MyIntentService﹕ onCreate pid:1784    thread id:1
06-11 09:45:35.777    1784-1809/? I/MyIntentService﹕ onHandleIntent pid:1784    thread id:170

在这个例子中,会发现,当IntentService处理完一个请求后,确实会将自己销毁。若此下一个请求来了,会再重新创建一个。所以发现两次的thread id不一样。

IntentService和Service一个重大的差别是在于,IntentService是会开一个工作线程来处理作务,而Service不会,因此Service中不能执行非常耗时的操作,否则会抛异常。下面便是一种验证方式:

@Override
public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
    Log.i(TAG, "onStart");
    //以下代码写在Service的onStart方法中会抛异常,但写在IntentService的onHandleIntent中不会。
    try {
        Thread.sleep(20000); 
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2 PendingIntent

PendingIntent 是对Intent的一个包装,用来处理未来发生的事。PendingIntent中会保存当前app的Context, 即使当前app已经停止了,仍然可以通过PendingIntent中的Context来执行Intent,通常与AlarmManagerNotificationManager在一起使用。这里有一个示例:

public void testPendingIntent() {
    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    long when = System.currentTimeMillis();// 通知发生的时间为系统当前时间
    int icon = android.R.drawable.stat_notify_chat;
    //第一个参数为图标 , 第二个参数为短暂提示标题 , 第三个为通知时间
    Notification notification = new Notification(icon, "TEST Notification", when);
    notification.defaults = Notification.DEFAULT_SOUND;// 发出默认声音
    notification.flags = Notification.FLAG_AUTO_CANCEL;// 点击后自动清除通知
    Intent openintent = new Intent(this, BActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, openintent, 0);
    notification.setLatestEventInfo(this, "This is title", "Content", contentIntent);
    manager.notify(0, notification);// 第一个参数为自定义的通知唯一标识
}

3 输入字符转换

之前有这么一个需求,某个EditText中接收的英文字母只能是大写的。当看到这个需求,我们的第一反应是设对这个EditText设置其键盘弹出属性android:inputType="textCapCharacters"。但是既使这样,用户是可以切换到小写模式下。当然或许我们还可以尝试其它的属性,但是想要统一各种机子上输入法的控制是比较难的,确实有些机子的输入法被产商弄得很奇葩,不受你的控制。我们可以用以下这种方式,自动将其转为大写的样式。

定义一个Util类

public class InputLowerToUpperTrans extends ReplacementTransformationMethod {
    @Override
    protected char[] getOriginal() {
        char[] lower = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' };
        return lower;
    }

    @Override
    protected char[] getReplacement() {
        char[] upper = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' };
        return upper;
    }
}

调用处

etTest.setTransformationMethod(new InputLowerToUpperTrans());

4 LocalBroadcastManager

在Android中,对于大部分app而言,发送广播时,只希望广播被自己app内部的receiver监听到,此时我们可以采用LocalBroadcastManager发送广播,安全性高,效率也高。 具体使用: 1. 写一个BroadcastReceiver, 和通常写法一样

public class MyReceiver extends BroadcastReceiver{
    public final static String TAG = "MyReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "------onReceive------");
    }

}

2. 注册我把它写到onResume里面

@Override
protected void onResume() {
    super.onResume();        
    myReceiver = new MyReceiver();
    IntentFilter filter = new IntentFilter();  
    filter.addAction(ACTION); //ACTION是自己定义的一个成员变量
    LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(myReceiver, filter);
}

3. 解注册

@Override
protected void onStop() {
    super.onStop();
    LocalBroadcastManager.getInstance(MainActivity.this).unregisterReceiver(myReceiver);
}

4. 发广播

Intent intent = new Intent();
intent.setAction(ACTION);//ACTION是自己定义的一个成员变量
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);