This project was actually written last year, but at that time it didn't use a base class. Instead, it used the Netease Youdao Translation SDK, and the coupling aspect was not done very well. During the winter vacation, I refactored it and used RxJava2 and Retrofit for network requests, as well as ButterKnife. I consider this project as my own learning experience, and I will gradually update it with any areas that can be optimized.
Project link: starTranslation
Key Points#
Using Retrofit and RxJava2 together#
1. Creating a Service class
Since we are using RxJava, the return type is no longer a Call, but an Observable.
public interface networkApi {
@GET("api?")
Observable<TranslationBean> translateYouDao(
@Query("q") String q,
@Query("from") String from,
@Query("to") String to,
@Query("appKey") String appKey, //Application ID
@Query("salt") String salt, //UUID
@Query("sign") String sign, //Application ID+input+salt+curtime+Application Secret. input= first 10 characters of q+length of q+last 10 characters of q (if length of q >= 20) or input = string
@Query("signType") String signType, //Signature type
@Query("curtime") String curtime //Timestamp
);
}
2. Creating the request process
public class netWork {
private static networkApi sContactsApi;
private static OkHttpClient okHttpClient = new OkHttpClient();
private static Converter.Factory gsonConverterFactory = GsonConverterFactory.create();
private static CallAdapter.Factory rxJavaCallAdapterFactory = RxJava2CallAdapterFactory.create();
private static class ApiClientHolder {
public static final netWork INSTANCE = new netWork();
}
public static netWork getInstance() {
return ApiClientHolder.INSTANCE;
}
public networkApi getDataService() {
if (sContactsApi == null) {
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(Constants.BASE_URL)
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
sContactsApi = retrofit.create(networkApi.class);
}
return sContactsApi;
}
}
3. Sending requests and handling data
@SuppressLint("CheckResult")
public void netConnection(String q,String from,String to,String salt,String sign,String curtime){
netWork.getInstance().getDataService()
.translateYouDao(q,from,to,appID,salt,sign,signType,curtime)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<TranslationBean>() {
@Override
public void accept(TranslationBean translationBean) throws Exception {
List<TranslationBean> list_word = new ArrayList<>();
list_word.add(translationBean);
mView.showResult(list_word);
}
});
}
4. Further improvements
The above code is based on the code I learned from my senior, and the encapsulation is not very good. When searching for other usage methods, I found someone else's encapsulation method, which I need to review when I have time.
Here is the other person's code: Android Elegant Use of RxJava2.0+Retrofit2.0
About Toolbar#
Toolbar is a powerful control, and basically every Activity needs it. Previously, I used to write a toolbar in every layout and initialize the view with ButterKnife, but this approach was very cumbersome... So I encapsulated the toolbar in BaseActivity. As for the layout, I first wrote a toolbar layout according to my own needs, and then included it where needed (I also did this for the button to switch ViewPager pages).
How to use it
First, create a toolbar layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="##1B6FB3"
android:layout_alignParentTop="true"
android:id="@+id/mtoolbar"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</androidx.appcompat.widget.Toolbar>
Then include it where needed
<include
layout="@layout/view_toolbar"/>
The same method can be used to create tab bars, etc.
Using Room#
Since I hadn't upgraded the database before when using Room, this was the first time I encountered this issue. After modifying the table, I understood from the error message that I needed to upgrade the version. However, after upgrading the version, I encountered the following error:
java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
What is this? I copied and pasted it to Google and found two solutions.
Increase the version and use fallback migration (data will be cleared)
private static wordDatabase buildDatabase(Context context) {
return Room.databaseBuilder(context.getApplicationContext(), wordDatabase.class, "StarWord.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration() //When upgrading the database, it will be rebuilt and the data will be cleared
.build();
}
In this case, when Room starts, it checks if the version has increased. If it has, the contents of the database will be cleared and the tables will be recreated.
Increase the version and provide a Migration (data will be preserved)
I didn't use this method because I don't plan to modify the database again, but I still want to learn it.
//Add a migration from version 1 to 2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
// Here you write the table modification
//database.execSQL("ALTER TABLE Starword " + " ADD COLUMN test INTEGER"); // Add a column named "test" to the table
}
};
Then add this migration to the databaseBuilder
private static wordDatabase buildDatabase(Context context) {
return Room.databaseBuilder(context.getApplicationContext(), wordDatabase.class, "StarWord.db")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2)
.build();
}
Now the database table is updated and the old data is also preserved.
Self-reflection#
- At the beginning, when clicking on a word in the favorite list, I planned to make the viewpager jump back to the first page and perform a new search. But I couldn't figure out how to jump using the view returned by the adapter, so I came up with the alternative method of displaying a dialog. I still haven't found a solution...
- Although there is a Retrofit data entity class, I still wrote a Room data class to store the data. If possible, I would like to combine them into one class to reduce code duplication.
- Some details were not done well, and overall, the app still lacks liveliness.
That's it for now. I will continue to add more if I encounter any more issues in the future.