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.
<application
android:name=".MyApplication"
...
</application>
这样在封装的工具类中就可以使用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活动等进行限制.
从图上可以看出, 手机退出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<Person> CREATOR = new Parcelable.Creator<Person>() {
@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按钮, 就会自动进入多窗口模式.
在项目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的属性来禁用多窗口模式:
<activity android:name=".MainActivity"
android:resizeableActivity="false"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
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类型. 是不是很方便?
喜欢和关注都是对我的鼓励和支持~