Android开发入门小知识

1.获取全局Context

通过Context, 可以启动服务, Activity, 通知, 创建操控内容提供器(ContentProvider)的对象SQLiteOpenHelper等. 在Activity中可以很容易获取Context, 但是在自己封装的工具类中, 就不能像Activity中使用继承至父类方法getContext来获取. 通用的方法是获取Application的Context.

首先新建继承自Application的类MyApplication.

public class MyApplication extends Application {

    private static Context context;
    @Override
    public void onCreate() {
        super.onCreate();

        context = getApplicationContext();
    }

    public static Context getContext() {
        return context;
    }
}

然后再AndroidManifest中使用新建的MyApplication.


这样在封装的工具类中就可以使用MyApplication的getContext方法, 轻易获取全局Context.

public class HttpUtil {

    public static void sendHttpRequest(final String address, final HttpCallbackListener listener){

        if (!isNetworkAvailable()){
            Toast.makeText(MyApplication.getContext(), "noNet", Toast.LENGTH_SHORT).show();
            return;
        }
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        HttpURLConnection connection = null;
                        try {
                            URL url = new URL(address);
                            connection = (HttpURLConnection) url.openConnection();
                            connection.setRequestMethod("GET");
                            connection.setConnectTimeout(8000);
                            connection.setReadTimeout(8000);
                            connection.setDoInput(true);
                            connection.setDoOutput(true);
                            InputStream in = connection.getInputStream();

                            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                            StringBuilder response = new StringBuilder();
                            String line;
                            while ((line = reader.readLine()) != null){
                                response.append(line);
                            }

                            if (listener != null){
                                listener.onFinish(response.toString());
                            }
                        }catch (Exception e){
                            if (listener != null){
                                listener.onError(e);
                            }
                        }finally {
                            if (connection != null){
                                connection.disconnect();
                            }
                        }
                    }
                }
        ).start();
    }

    private static boolean isNetworkAvailable(){
        return true;
    }
}

2.实现自己的Log日志工具类

开发过程中, 我们常常借助打印出的详细日志来调试程序, 可以帮助自己理顺开发逻辑思路. 但是, 如果发布之后就没有必要再次打印, 不然会在后台消耗大量的手机资源, 给用户造成App是耗电大户的假象. 解决方式是可以自定义日志工具类, 通过参数控制日志是否打印.

public class LogUtil {

    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int WARN = 4;
    public static final int ERROR = 5;
    public static final int NOTHING = 6;
    public static final int level = VERBOSE;

    public static void v(String tag, String msg){
        if (level <= VERBOSE){
            Log.v(tag, msg);
        }
    }

    public static void d(String tag, String msg){
        if (level <= DEBUG){
            Log.d(TAG, msg);
        }
    }

    public static void i(String tag, String msg){
        if (level <= INFO){
            Log.i(TAG, msg);
        }
    }

    public static void w(String tag, String msg){
        if (level <= WARN){
            Log.w(TAG, msg);
        }
    }

    public static void e(String tag, String msg){
        if (level <= ERROR){
            Log.e(TAG, msg);
        }
    }
}

调试状态下, 把level设置成VERBOSE的级别, 打印出所有日志; 发布时, 将level设置成NOTHING级别, 不打印日志.

3.后台定时执行

很多时候, 作为前端的App需要和服务端定时通信, 比如行情数据的刷新、报价的定时提醒等. Android中通过AlarmManager可以实现.

public class LongRunningService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("LongRunningService", "run: ");
            }
        }).start();

        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
        int dur = 5 * 1000;
        long triggerAtTime = SystemClock.elapsedRealtime() + dur;
        Intent intent1 = new Intent(this, LongRunningService.class);

        PendingIntent pi = PendingIntent.getService(this, 0, intent1, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);

        return super.onStartCommand(intent, flags, startId);
    }
}

通过SystemClock.elapsedRealtime()可以获取自从Android开机累计的时间. 通过System.currentTimeMillis()可以获取自从1970年1月1日0点( UNIX和C语言诞生)开始累计的时间. manager的set方法接受3个参数, 第一个参数表示启用的时间起点: ELAPSED_REALTIME_WAKEUP表示使用开机累计的时间, RTC表示1970年累计的时间. 第二个参数表示触发的时间点. 第三个参数表示要启动的PendingIntent.

通过以上设置, 就能在启用LongRunningService后, 每隔5s执行Thread中的run方法.

4.Doze模式

为了限制Android后台服务过多造成的耗电过快问题, Android6.0推出了Doze模式. 在Doze模式下, 系统会对CPU、网络、Alarm活动等进行限制.

Android开发入门小知识_第1张图片
Doze模式

从图上可以看出, 手机退出Doze模式间隔的时间逐渐延长, 因为这时可以认为用户与手机交互的需要逐渐降低. 如果在Doze模式下要求Alarm强制执行, 可以使用AlarmManager的setAndAllowWhileIdle或者setExactAndAllowWhileIdle方法.

5.0 使用Intent传递对象

我们知道通过Intent可以传递String, int等基本数据类型, 但是如果是class对象, 该如何传递呢? 有Serializable和Parcelable两种方式.

<1>Serializable

public class Person implements Serializable {
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

然后就可以用Intent来传递:

Person person = new Person();
person.setAge(18);
person.setName("xiaoming");
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("person", person);
startActivity(intent);

在SecondActivity中的获取方式:

Person person = (Person) intent.getSerializableExtra("person");
Log.d(TAG, "onCreate: " + person.getName() + ", " + person.getAge());

<2>Parcelable
Android推出Parcelable是为了解决使用Serializable存取对象效率过慢的问题, 将存取对象的地方从硬盘中提升到内存中进行. 它也通过接口的方式来实现.

public class Person implements Parcelable {
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeInt(age);
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public Person createFromParcel(Parcel parcel) {

            Person person = new Person();
            person.name = parcel.readString();
            person.age = parcel.readInt();
            return person;
        }

        @Override
        public Person[] newArray(int i) {
            Log.d(TAG, "newArray11111: " + i);
            return new Person[i];
        }
    };
}

传递方式和Serializable方式一样, 获取方式有所区别.

Person person = (Person) intent.getParcelableExtra("person");
Log.d(TAG, "onCreate: " + person.getName() + ", " + person.getAge());

6. 多窗口模式
在Android7.0下, 长按底部控制栏最右边的OverView按钮, 就会自动进入多窗口模式.

Android开发入门小知识_第2张图片
多窗口模式

在项目MaterialTest3的MainActivity中, 我们在页面的生命周期中添加打印消息, 来观察多窗口模式下页面的生命周期.

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MaterialTest3";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();

        Log.d(TAG, "onStart: ");
    }

    @Override
    protected void onResume() {
        super.onResume();

        Log.d(TAG, "onResume: ");
    }

    @Override
    protected void onPause() {
        super.onPause();

        Log.d(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();

        Log.d(TAG, "onStop: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        Log.d(TAG, "onDestroy: ");
    }

    @Override
    protected void onRestart() {
        super.onRestart();

        Log.d(TAG, "onRestart: ");
    }
}

观察打印日志:

D/MaterialTest3: onPause: 
D/MaterialTest3: onStop: 
D/MaterialTest3: onDestroy: 
D/MaterialTest3: onCreate: 
D/MaterialTest3: onStart: 
D/MaterialTest3: onResume: 
D/MaterialTest3: onPause: 

可以看到界面实际上是重新销毁, 二次创建的过程.

通过设置MainActivity的属性来禁用多窗口模式:


   
       
       
   

7.Lambda表达式

Android是基于Java的语言, Java 8.0的发布也给Android的开发带来了便利. Java8.0支持Lamda表达式、streamAPI、接口默认实现等. streamAPI和接口默认实现只支持Android 7.0以上的系统, 而Lamda表达式最低支持Android 2.3, 值得学习一下.

首先需要在app的build.gradle文件中添加Java8的支持.

defaultConfig {
    jackOptions.enabled = true
   ...
}
compileOptions{
    sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8
    targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8
}

然后我们可以很方便的实现只有一个方法的接口. 首先定义接口:

public interface MyInterface_Lambda {
    public String speak(String a, String b);
}

然后可以如下实现和调用接口:

MyInterface_Lambda myInter = (a, b) -> {
            return a + b;
        };
myInter.speak("Hello ", "world!");

其中Lambda表达式可以自动推导出参数a, b 为String类型. 是不是很方便?

喜欢和关注都是对我的鼓励和支持~

你可能感兴趣的:(Android开发入门小知识)