• 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用来管理活动——随时退出程序

  • 相关阅读:
    jdbc(插入大对象及读取大对象、存储过程)
    jdbc批量插入操作(addBatch)
    javase(Properties集合及学生对象信息录入文本中案例)
    javase模拟斗地主洗牌和发牌(54)
    javase套接字编程
    javase网络编程
    javase多线程复制
    javase文件切割及融合
    设计原则
    模板方法模式
  • 原文地址:https://www.cnblogs.com/erhai/p/4953720.html
Copyright © 2020-2023  润新知