Android——实用小技巧

一、获取全局Context——解决Toast找不到可用Contex的尴尬

Application类,当应用程序启动的时候,系统将会对这个类初始化,可以定制一个Application类,管理程序全局状态信息,如Context

定制Application

 1 package com.example.contexttest;
 2 
 3 import android.app.Application;
 4 import android.content.Context;
 5 public class MyApplication extends Application {
 6     private static Context context;
 7 
 8     @Override
 9     public void onCreate() {
10         context = getApplicationContext();
11     }
12     
13     public static Context getContext(){
14         return context;
15     }
16     
17 }

使用全局Context

 1 package com.example.contexttest;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.InputStream;
 5 import java.io.InputStreamReader;
 6 import java.net.HttpURLConnection;
 7 import java.net.URL;
 8 
 9 import android.widget.Toast;
10 
11 public class HttpUtil {
12     public static void sendHttp(final String address,
13             final HttpListener listener) {
14         // 在这里我们判断一下网络是否可用,如果不可用,则弹出提示
15         // 问题来了,Toast需要传递Context,在哪里去找一个Context?——通过自定义application
16         if(!isNetWorkAvailable()){
17             Toast.makeText(MyApplication.getContext(), "network is unavailable", Toast.LENGTH_SHORT).show();
18         }
19         
20         new Thread(new Runnable() {
21             HttpURLConnection connection = null;
22 
23             @Override
24             public void run() {
25                 try {
26                     // 获取HttpURLConnection对象
27                     URL url = new URL(address);
28                     connection = (HttpURLConnection) url.openConnection();
29                     
30                     // 设置请求方式和延迟
31                     connection.setRequestMethod("GET");
32                     connection.setConnectTimeout(8000);
33                     connection.setReadTimeout(8000);
34                     connection.setDoInput(true);
35                     connection.setDoOutput(true);
36                     
37                     // 请求数据,疯狂的读
38                     StringBuilder response = new StringBuilder();
39                     InputStream in = connection.getInputStream();
40                     BufferedReader bufr = new BufferedReader(new InputStreamReader(in));
41                     String line = null;
42                     while((line=bufr.readLine())!=null){
43                         response.append(line);
44                     }
45                     
46                     // 读完之后通过监听器操作数据
47                     if(listener!=null){
48                         listener.onFinish(response.toString());
49                     }
50                     
51                     
52                 } catch (Exception e) {
53                     listener.onError(e);
54                 } finally {
55                     if (connection != null) {
56                         connection.disconnect();
57                     }
58                 }
59             }
60         }).start();
61     }
62 }

有时候会java.lang.ClassCastException: android.app.Application cannot be cast to  XXXX.xxApplication的错误

需要在AndroidManifest中注册 一下application,在原有的application中添加一项

<application
        android:name="XXXX.xxApplication"

二、Inten传递对象

intent除了可以传递常见的数据类型如int,boolean等等,但是不能直接传递对象,要传递对象,通常有两种做法

1.序列化——Serializable

序列化很简单,只需要让类实现Serializable接口就可以了,并且这个接口一个方法都没有!!!仅仅相当于是添加一个标记一样!

 1 package com.example.intenttest;
 2 
 3 import java.io.Serializable;
 4 
 5 public class People implements Serializable {
 6     private String Name;
 7 
 8     public People() {
 9     }
10 
11     public People(String name) {
12         super();
13         Name = name;
14     }
15 
16     public String getName() {
17         return Name;
18     }
19 
20 }

传入:

1                 Intent intent = new Intent(MainActivity.this,SecondActivity.class);
2                 People people = new People("秋香");
3                 intent.putExtra("people", people);
4                 startActivity(intent);

取出:

People people = (People) getIntent().getSerializableExtra("people");
textView.setText("Serializable:"+people.getName()+" ");

Serializable方式会将整个对象序列化,效率上会比parcelable稍低

2.Parcelable方式

 1 package com.example.intenttest;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 
 6 public class Dog implements Parcelable {
 7     private String name;
 8     private int age;
 9 
10     public Dog() {}
11     public Dog(String name, int age) {
12         super();
13         this.name = name;
14         this.age = age;
15     }
16 
17     public String getName() {
18         return name;
19     }
20 
21     public int getAge() {
22         return age;
23     }
24 
25     /**
26      * 返回0即可
27      */
28     @Override
29     public int describeContents() {
30         // TODO Auto-generated method stub
31         return 0;
32     }
33 
34     /**
35      * 将字段一一写出
36      */
37     @Override
38     public void writeToParcel(Parcel dest, int flags) {
39         dest.writeString(name);
40         dest.writeInt(age);
41 
42     }
43 
44     /**
45      * Parcelable方式必须提供一个CREATOR常量,传入泛型类名
46      */
47     public static final Parcelable.Creator<Dog> CREATOR = new Creator<Dog>() {
48 
49         /**
50          * 指定数组大小
51          */
52         @Override
53         public Dog[] newArray(int size) {
54             // TODO Auto-generated method stub
55             return new Dog[size];
56         }
57 
58         /**
59          * 按照写入的顺序一一读取
60          */
61         @Override
62         public Dog createFromParcel(Parcel source) {
63             // TODO Auto-generated method stub
64             Dog dog = new Dog();
65             dog.name = source.readString();
66             dog.age = source.readInt();
67             return dog;
68         }
69     };
70 }

存入:

1         Intent intent = new Intent(MainActivity.this,SecondActivity.class);
2         Dog dog = new Dog("旺财", 8);
3         intent.putExtra("dog", dog);
4         startActivity(intent);

取出:

1 Dog dog = getIntent().getParcelableExtra("dog");
2 textView.setText("Parcelable:"+dog.getName()+"::"+dog.getAge()+"
");

Parcelable稍微复杂一点,不过效率稍微高一点,更推荐使用

 三、定制自己的Log工具

为了是正式发布的软件不输出Log,开发测试时才输出Log,可以定义一个LogUtil工具类来控制!!

很简单,很强大!!

 1 package com.example.utils;
 2 
 3 import android.util.Log;
 4 
 5 /**
 6  * 
 7  * 在开发阶段,可以将Level设置成1,这样就和Log功能一样.<br/>
 8  * 在发布时,Level设置成NOTHING,这样就不会有任何Log了.<br/>
 9  */
10 public class LogUtil {
11     public static final int VERBOSE = 1;
12     public static final int DEBUG = 2;
13     public static final int INFO = 3;
14     public static final int WARN = 4;
15     public static final int ERROR = 5;
16     public static final int NOTHING = 6;
17     public static final int LEVEL = VERBOSE;
18 
19     /**
20      * 打印全部
21      */
22     public static void v(String tag, String msg) {
23         if (LEVEL <= VERBOSE) {
24             Log.v(tag, msg);
25         }
26     }
27 
28     public static void d(String tag, String msg) {
29         if (LEVEL <= DEBUG) {
30             Log.d(tag, msg);
31         }
32     }
33 
34     public static void i(String tag, String msg) {
35         if (LEVEL <= INFO) {
36             Log.i(tag, msg);
37         }
38     }
39 
40     public static void w(String tag, String msg) {
41         if (LEVEL <= WARN) {
42             Log.w(tag, msg);
43         }
44     }
45 
46     public static void e(String tag, String msg) {
47         if (LEVEL <= ERROR) {
48             Log.e(tag, msg);
49         }
50     }
51 
52 }

四、eclipse调试Android程序

1.设置断点——在需要调试的开始行双击——取消也是双击

  

2.将程序跑起来,到需要调试的位置为止

3.进入DDMS

  

4.选中测试机器的包名进程——最下面一行,点击上方绿色小蜘蛛,成功后包名前面会多出一个小蜘蛛

  

5.继续执行程序,就会出现Debug了

  

  

6.F6逐行执行

  

7.查看变量值

  

8.停止调试——点击红框按钮即可

  

这样做的好处在于,可以随时进入程序调试。所以并没用Debug as 来执行程序,而是直接run as

 五、编写测试用例

必要性在于:当开发的项目规模大的时候,测试用例格外重要,每当修改增加了某功能后,都应该把测试用例跑一遍!!!

测试用例也不过是一段代码而已,一般一个用例测试一个小单元的功能。

1.创建测试工程

File->New->Other->Android Test Project

  

  

工程名直接为要测试的工程加个Test后缀即可,路径也是该工程目录下新建tests文件即可

  

选择已存在的该工程,finish即可  

  

创建成功之后,就会多出一个工程,这就是测试工程了!!

在这个工程里面编写测试用例即可!

  

2.编写测试用例进行单元测试

下面定义一个AndroidControllerTest 类,用于对BroadcastBestPractice项目中AndroidController类测试

  需要继承AndroidTestCast方法

  需要重写setUp和tearDown方法,可对测试用例执行初始化和资源回收操作

  要对AndroidController的addActivity方法测试,只需要创建一个方法,testaddActivity(对!就是在这个方法前面加一个test即可!)

  在测试方法中,通过断言assert,来判断期望的值和结果是否一致,一致则跑通,不一致,则说明程序有需要修改的地方

AndroidController.java

 1 /**
 2  * 活动控制器 1.添加活动 2.删除活动 3.销毁所有活动
 3  */
 4 public class ActivityController {
 5     public static List<Activity> activities = new ArrayList<Activity>();
 6 
 7     public static void addActivity(Activity activity) {
 8             activities.add(activity);
 9     }
10 
11     public static void removeActivity(Activity activity) {
12 
13         activities.remove(activity);
14     }
15 
16     public static void finishAll() {
17         for (Activity activity : activities) {
18             activity.finish();
19         }
20     }
21 }

AndroidControllerTest.java

 1 package com.example.broadcastbestpractic.test;
 2 
 3 import com.example.broadcastbestpractic.ActivityController;
 4 import com.example.broadcastbestpractic.LoginActivity;
 5 
 6 import android.test.AndroidTestCase;
 7 /**
 8  * 
 9  * BroadcastBestPractice中的AndroidController类测试用例.<br/>
10  * 需要重写setUp和tearDown方法
11  */
12 public class AndroidControllerTest extends AndroidTestCase {
13 
14     /**
15      * 测试用例调用前执行,可以在这里进行初始化操作
16      */
17     @Override
18     protected void setUp() throws Exception {
19         // TODO Auto-generated method stub
20         super.setUp();
21     }
22     
23     /**
24      * 在需要测试的方法前面加上test,系统就会自动测试该方法.</br>
25      * 通过assert进行断言.<br/>
26      * Run as Android JUit Test运行测试用例.<br/>
27      */
28     public void testaddActivity(){
29         // 断言activity结合的数量初始为0
30         assertEquals(0, ActivityController.activities.size());
31         LoginActivity loginActivity = new LoginActivity();
32         ActivityController.addActivity(loginActivity);
33         // 断言经过addActivity方法操作之后,活动个数增加成为1,被认为是正确的
34         assertEquals(1, ActivityController.activities.size());
35         
36     }
37     
38 
39     /**
40      * 测试用例执行完成时执行,可以在这里进行资源的释放
41      */
42     @Override
43     protected void tearDown() throws Exception {
44         // TODO Auto-generated method stub
45         super.tearDown();
46     }
47     
48 }

Run as Android JUit Test , 发现能够跑通,说明满足测试用例(绿色

然后,对AndroidControllerTest.java这个类进行一下修改

 1     public void testaddActivity(){
 2         // 断言activity结合的数量初始为0
 3         assertEquals(0, ActivityController.activities.size());
 4         LoginActivity loginActivity = new LoginActivity();
 5         ActivityController.addActivity(loginActivity);
 6         // 断言经过addActivity方法操作之后,活动个数增加成为1,被认为是正确的
 7         assertEquals(1, ActivityController.activities.size());
 8         
 9         ActivityController.addActivity(loginActivity);
10         // 断言再次执行addActivity时,活动个数仍然是1,即不重复加载
11         assertEquals(1, ActivityController.activities.size());
12         
13     }

在此Run一下,发现不能跑通,

提示期望是1,但是结果是2

发现AndroidController不符合测试用例

修改AndroidController的代码

1         if (!activities.contains(activity)) {
2             activities.add(activity);
3         }

然后在Run一下,发现能够跑通了!!

 六、在服务中Toast

 直接写则:RuntimeException:Can't creat handler inside thread that has not called Looper.prepare()

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 输出当前时间
        new Thread(new Runnable() {

            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(LongRunningService.this, "executed at:"
                                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), Toast.LENGTH_SHORT).show();
                Looper.loop();  
            }
        }).start();

完整:

 1 public class MainActivity extends Activity {
 2 
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.activity_main);
 7         Intent service = new Intent(this, LongRunningService.class);
 8         startService(service);
 9     }
10 
11 }
MainActivity
 1 public class LongRunningService extends Service {
 2 
 3     @Override
 4     public IBinder onBind(Intent intent) {
 5         return null;
 6     }
 7 
 8     @Override
 9     public int onStartCommand(Intent intent, int flags, int startId) {
10         // 输出当前时间
11         new Thread(new Runnable() {
12 
13             @Override
14             public void run() {
15                 Looper.prepare();
16                 Toast.makeText(LongRunningService.this, "executed at:"
17                                 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), Toast.LENGTH_SHORT).show();
18                 Looper.loop();  
19             }
20         }).start();
21         // 定时打开广播接收器——10s一次
22         AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
23         /*
24          * type还可以用RTC,RTC_WAKEUP,对应的触发时间应该使用System.currenTimetMillis()
25          */
26         // (int type, long triggerAtMillis, PendingIntent operation)
27         int type = AlarmManager.ELAPSED_REALTIME_WAKEUP; // 从系统开机时累积的总时间
28         long triggerAtMillis = SystemClock.elapsedRealtime() + 10 * 1000;
29         intent = new Intent(this, ServiceReceiver.class);
30         PendingIntent operation = PendingIntent.getBroadcast(this, 0, intent, 0);
31 
32         // 通过set定时执行可能会延迟,4.4之后,因为手机会有省电设计,如果要准确无误,用setExact()
33         alarmManager.set(type, triggerAtMillis, operation);
34 
35         return super.onStartCommand(intent, flags, startId);
36     }
37 
38     @Override
39     public void onDestroy() {
40         super.onDestroy();
41     }
42 
43 }
LongRunningService
 1 public class ServiceReceiver extends BroadcastReceiver {
 2 
 3     @Override
 4     public void onReceive(Context context, Intent intent) {
 5         // 被激活则直接开启服务
 6         Intent service = new Intent(context, LongRunningService.class);
 7         context.startService(service);
 8     }
 9 
10 }
ServiceReceiver

七、知晓当前是在哪一个活动

思路是:写一个BaseActivity类继承Activity,新增活动创建时打印日志功能,让所有需要关注的活动继承BaseActivity即可,当不在需要知晓当前活动时,去掉log即可获继承回Activity

 1 public class BaseActivity extends Activity {
 2 
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         LogUtil.d("BaseActivity", getClass().getSimpleName());
 7         ActivityCollector.addActivity(this);
 8     }
 9     
10     @Override
11     protected void onDestroy() {
12         super.onDestroy();
13         ActivityCollector.removeActivity(this);
14     }
15     
16 }
 1 public class ActivityCollector {
 2 
 3     public static List<Activity> activities = new ArrayList<Activity>();
 4 
 5     public static void addActivity(Activity activity) {
 6         activities.add(activity);
 7     }
 8 
 9     public static void removeActivity(Activity activity) {
10         activities.remove(activity);
11     }
12 
13     public static void finishAll() {
14         for (Activity activity : activities) {
15             if (!activity.isFinishing()) {
16                 activity.finish();
17             }
18         }
19     }
20 
21 }
ActivityCollector.java

ActivityCollector用来管理活动——随时退出程序

原文地址:https://www.cnblogs.com/erhai/p/4953720.html