星空直播平台安卓端开发的一些总结
项目地址:https://github.com/xingkongus/stream-watch-android
项目的开始¶
星空直播平台的后端整个算是翰新搭建起来的。他给我后端的API接口,我试着把他封装成SDK,并且做一个安卓端的Demo出来。于是项目就开始了......
一些知识的总结¶
Fragment全局获取上下文联系Context¶
在封装SDK的过程中,我用SharedPreferences来存储从服务器获取的Cookie,里面保存和获取保存的Cookie的方法为:
public static void saveCookiePreference(Context context, String value) {
SharedPreferences preference = context.getSharedPreferences(ISLOGINED, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preference.edit();
editor.putString(COOKIE, value);
editor.apply();
}
public static String getCookiePreference(Context context) {
SharedPreferences preference = context.getSharedPreferences(ISLOGINED, Context.MODE_PRIVATE);
return preference.getString(COOKIE, "");
}
很明显我需要用到Context,所以我在实例化操作API的对象时需要传入一个参数:
但是我在Fragment里面需要实例化Cilent类时,不管是利用Fragment类的getActivity()还是getContext()方法,SharedPreferences的操作总是会报错,大致的意思就是没有办法获取Context。而AS也会有getActivity()或getContext()返回值可能为空的警告。我不知道为什么,但是就干脆自己写一个获取全局变量的方法。
首先写一个自定义的Application类MyApplication,写法如下:
public class MyApplication extends Application {
/**
* 一个MyApplication的静态对象
* 用于Fragment获取上下文练习Context
*/
private static MyApplication mInstance;
@Override
public void onCreate() {
super.onCreate();
mInstance = this; //在MyApplication 启动时获取本对象
......
}
/**
* 获取context
*
* @return mInstance
*/
public static Context getInstance() {
return mInstance;
}
}
以此便可以获取当前的Application实例,然后写BaseFragment:
public class BaseFragment extends Fragment {
/**
* 一个Activity对象作为Context
*/
private Activity activity;
/**
* 子Fragment获取Context的方法
* @return activity
*/
public Context getInstantContext() {
if (activity == null) {
return MyApplication.getInstance();
}
return activity;
}
/**
* 用Fragment的onAttach(Context context);方法来绑定Context
* @param context
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
activity = getActivity();
}
}
随后的Fragment继承BaseFragment,实例化Client时调用getInstantContext()方法即可。
ActivityLifecycleCallbacks接口的使用¶
现在很多安卓开发的过程中都会用到Toolbar这一个强大的控件,但是一般的App都不止由一个Activity组成,如果在写每个Activity时都要实例化一个Toolbar显然不高效。
我原本的实现方式是封装一个BaseActivity,在里面实现Toobar然后将子Activity的布局用LayoutInflater投射到BaseActivity布局的容器里。
然而,我在开发的过程中引入了ButterKnife,一个很好用的依赖注入框架。但是问题也出现了:继承BaseActivity的子Activity中的布局,可能是由LayoutInflater投射到容器的原因,ButterKnife无法获取布局中的控件值。
在试了很多方法都无法解决之后,我突然想起在有道云笔记收藏的一篇文章:我一行代码都不写实现Toolbar!你却还在封装BaseActivity?;文章不提倡封装BaseActivity,因为会有管理困难的问题。在我此次开发也会有这样的问题:并非所有的Activity都需要Toobar,但是其可能需要一些其他的对象或着操作。
而文章的解决办法用到了Application类内部的一个接口ActivityLifecycleCallbacks和方法registerActivityLifecycleCallbacks()来解决。作用就是就是在你调用这个方法传入这个接口实现类后,系统会在每个 Activity 执行完对应的生命周期后都调用这个实现类中对应的方法,注意是每个
具体的用法¶
先按照自己的需求单独写一个toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/us.xingkong.testing"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll||enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
</android.support.v7.widget.Toolbar>
在需要的地方include:
那么回到自定义的MyApplication:
public class MyApplication extends Application {
......
@Override
public void onCreate() {
super.onCreate();
......
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.findViewById(R.id.toolbar)!=null){
((BaseActivity)activity).setSupportActionBar((Toolbar)activity.findViewById(R.id.toolbar));
}
}
......
});
}
......
}
在Activity中:
public abstract class BaseActivity extends AppCompatActivity {
......
@Override
protected void onCreate(final Bundle savedInstanceState) {
setContentView(getLayout());
super.onCreate(savedInstanceState);
......
}
......
}
注意:由于 ActivityLifecycleCallbacks 中所有方法的调用时机都是在 Activity 对应生命周期的 Super 方法中进行的,所以在 Activity 的 onCreate 方法中使用 setContentView 必须在 super.onCreate(savedInstanceState); 之前,不然在 onActivityCreated 方法中 findViewById 会发现找不到。(文章原话,这个坑我就踩了)
由此便可以不用BaseActivity来实现Toolbar。
关于项目的一些思考¶
-
总的来说我对此项目有点不满意的是由于才疏浅薄,我想不到一个更好的封装SDK封装方式,以至于需要在每个Activity创建的时候都实例一个Client对象。显然这还是比较低级的。暂时的想法可能会试着将其封装为用 Client.方法名 的静态方法的方式来调用。
-
没有使用结构,比如MVP、MVVM等。接下来还是重点学习一下MVP...
-
Java(HttpUrlConnection)是否已经有将Cookie存于本地的方法,这个有待于我去好好研究,毕竟如果本身已经有更高效的方法,就没有必要再用SharedPreferences。
-
依然不能明白Fragment中的getActivity()或getContext()返回值为何为空...
当然开发的过程中遇到了很多坑,等想到了再继续补充吧