Android(安卓)Android(安卓)Studio 3.3 项目中导入环信的 EaseUI Module
一、前言
最近看到有许多人在说根据环信官方文档导入 EaseUI 会出现各种错误,由于官方(此处和谐省略一万字), 顺便学学新的 add dependencies 方式,记录一下, Android Studio 3.3 add dependencies 方式又改了。
二、运行环境
Android Studio 版本 : 3.3 Canary 5
!
环信 SDK 版本 : easemob-sdk-3.5.0
三、导入 EaseUI
先新建个项目
下载 easemob-sdk 里面带有 EaseUI
导入这个 easeui
File -> New -> Import Module
选择 easeui 所在的路径
点击 Finish
导入之后会有红色的提示,四个都只是 WARNING,把四个 WANRNING 解决掉
第一个 WANRNING,是 Build Tools 的问题,Android Studio 3.3.0-alpha05 最低支持的 Build Tools 版本是 27.0.3 ,把 easeui 的 build.gradle 的 buildToolsVersion 改为 27.0.3 顺便把 compileSdkVersion 和 targetSdkVersion 也改为 27,同步一下 gradle,第一个 WANRNING 已经没了
后三个 WANRNING,是 AndroidStudio 3.0 之后的问题,
compile 改为 implementation 或 api,
implementation 和 api 是有区别的,implementation 是指这个导入的 Library 是 Module 内部使用的,别的 Module 导入当前这个 Module 就用不了当前 Module 导入的 Library 的东西,api 是指别的 Module 导入当前这个 Module 后仍然能够使用当前 Module 导入的 Library 的东西,
testCompile 改为 testImplementation,
androidTestCompile 改为 androidTestImplementation,
由于 easeui 的 libs 文件夹里有环信的 hyphenatechat_3.5.0.jar ,由于 app 里要用到 hyphenatechat 里的东西,所以 fileTree() 要用 api 而不是 implementation,改完后同步一下 gradle,剩下的三个 WANRNING 都没了
接下来 app 添加上 easeui 这个 Module ,在项目右键 Open Module Settings
选择 Dependencies,Modules 选择 app,Declared dependencies 点击 + 号
上面 step 1 的 easeui 打上勾,Step 2 选 implementation,点击 OK,再点 Apply 或 OK
由于 AndroidStudio 3.2 起开始推荐使用 androidx, 但 easeui 里用的是 android, 所以会报错,要么把 easeui 里的改为 androidx 要么不使用 androidx,改 easeui 的话那就太多要改的了,不使用 androidx 了,
打开 gradle.properties 注释掉 android.useAndroidX=true 和 android.enableJetifier=true
打开 app 的 build.gradle 也要改,compileSdkVersion, buildToolsVersion, targetSdkVersion 那些改和 easeui 一样的,dependencies androidx 的改为 android 的,改完同步一下 gradle, 由于去掉了 androidx, Activity 里的 AppCompatActivity 和 布局里的 ConstraintLayout 也要改为 android 的而不是 androidx 的
看起来好像没什么问题了,先运行一下,
出问题了,support v4 包没有 AsyncTaskCompat 这个类了,打开 EaseChatRowImage.java 这个类
去掉 AsyncTaskCompat,改直接执行 AsyncTask ,
再运行一下,可以运行起来
四、初始化 easeui
虽然导入了 easeui, 但 easeui 还没在 app 里初始化和用起来,先初始化 easeui
新建个 App 类继承 Application,然后在 AndroidManifest 里设置
public class App extends Application { @Override public void onCreate() { super.onCreate(); initEaseUi(); } private void initEaseUi() { EMOptions emOptions = new EMOptions(); emOptions.setAcceptInvitationAlways(false); EaseUI.getInstance().init(this, new EMOptions()); }}
再运行一次 , 报错了,看下 Log
说没有在 AndroidManifest 里设置 APPKEY,那就设置一下了,可参考官方的 环信官方配置工程
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.ce.easeuitest"> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.GET_TASKS" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:name=".App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> <meta-data android:name="EASEMOB_APPKEY" android:value="1111180606228105#easeuitest" /> <service android:name="com.hyphenate.chat.EMChatService" android:exported="true"/> <service android:name="com.hyphenate.chat.EMJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" /> <receiver android:name="com.hyphenate.chat.EMMonitorReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <data android:scheme="package"/> intent-filter> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.USER_PRESENT" /> intent-filter> receiver> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> intent-filter> activity> application>manifest>
再运行一次, 可以运行起来,再看 log 没有问题
五、简单使用 easeui
做个登录功能测试一下,新建个 LoginActivity
修改一下 LoginActivity 的代码逻辑并实现登录操作,为了直接点,这里就在 Activity 里去开启线程做登录操作了
/** * A login screen that offers login via email/password. */public class LoginActivity extends AppCompatActivity{ // UI references. private AutoCompleteTextView mEmailView; private EditText mPasswordView; private View mProgressView; private View mLoginFormView; private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); // Set up the login form. mEmailView = (AutoCompleteTextView) findViewById(R.id.email); mPasswordView = (EditText) findViewById(R.id.password); mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { attemptLogin(); return true; } return false; } }); Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); mEmailSignInButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { attemptLogin(); } }); mLoginFormView = findViewById(R.id.login_form); mProgressView = findViewById(R.id.login_progress); } @Override protected void onDestroy() { mHandler.removeCallbacksAndMessages(null); super.onDestroy(); } /** * Attempts to sign in or register the account specified by the login form. * If there are form errors (invalid email, missing fields, etc.), the * errors are presented and no actual login attempt is made. */ private void attemptLogin() { // Reset errors. mEmailView.setError(null); mPasswordView.setError(null); // Store values at the time of the login attempt. String email = mEmailView.getText().toString(); String password = mPasswordView.getText().toString(); boolean cancel = false; View focusView = null; // Check for a valid password, if the user entered one. if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { mPasswordView.setError(getString(R.string.error_invalid_password)); focusView = mPasswordView; cancel = true; } // Check for a valid email address. if (TextUtils.isEmpty(email)) { mEmailView.setError(getString(R.string.error_field_required)); focusView = mEmailView; cancel = true; } else if (!isEmailValid(email)) { mEmailView.setError(getString(R.string.error_invalid_email)); focusView = mEmailView; cancel = true; } if (cancel) { // There was an error; don't attempt login and focus the first // form field with an error. focusView.requestFocus(); } else { // Show a progress spinner, and kick off a background task to // perform the user login attempt. showProgress(true); // 开启线程去做登录操作 new LoginThread(email, password).start(); } } private boolean isEmailValid(String email) { return email.length() > 4; } private boolean isPasswordValid(String password) { return password.length() > 4; } /** * Shows the progress UI and hides the login form. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) private void showProgress(final boolean show) { // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow // for very easy animations. If available, use these APIs to fade-in // the progress spinner. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); mLoginFormView.animate().setDuration(shortAnimTime).alpha( show ? 0 : 1).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); } }); mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); mProgressView.animate().setDuration(shortAnimTime).alpha( show ? 1 : 0).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); } }); } else { // The ViewPropertyAnimator APIs are not available, so simply show // and hide the relevant UI components. mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); } } private class LoginThread extends Thread { String username; String password; public LoginThread(String username, String password) { this.username = username; this.password = password; } @Override public void run() { EMClient.getInstance().login(username, password, new EMCallBack() { @Override public void onSuccess() { mHandler.post(new Runnable() { @Override public void run() { showProgress(false); // 登录成功跳转去 MainActivity Intent intent = new Intent(LoginActivity.this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); finish(); } }); } @Override public void onError(int i, String s) { mHandler.post(new Runnable() { @Override public void run() { // 登录失败 showProgress(false); mPasswordView.setError(getString(R.string.error_invalid_password)); View focusView = mPasswordView; focusView.requestFocus(); } }); } @Override public void onProgress(int i, String s) { } }); } }}
修改下 Manifest,让 LoginActivity 先运行
<activity android:name=".LoginActivity" android:label="@string/title_activity_login"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> intent-filter>activity>
由于登录后跳转到 MainActivity ,那就简单地在 MainActivity 做个联系人列表显示,
新建个 ContactFragment 类继承 EaseContactListFragment,这里也简单点,用个 AsyncTask 去获取所有的联系人然后显示出来,
public class ContactFragment extends EaseContactListFragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); new GetContact().execute(); } private class GetContact extends AsyncTask<Void, Void, Map<String, EaseUser>> { @Override protected Map doInBackground(Void... voids) { try { Map contactMap = new HashMap<>(); List contacts = EMClient.getInstance().contactManager().getAllContactsFromServer(); for (String contact : contacts) { EaseUser easeUser = new EaseUser(contact); contactMap.put(contact, easeUser); } return contactMap; } catch (HyphenateException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Map easeUserMap) { if (easeUserMap != null && !easeUserMap.isEmpty()) { setContactsMap(easeUserMap); refresh(); } } }}
把 ContactFragment 放到 MainActivity 去显示,
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.lay_content, new ContactFragment()) .commitNowAllowingStateLoss(); }}
一切准备就绪,等等,还有账号还没准备,先注册几个账号,
添加几个好友进去
运行起来看看吧,可以登录并获取到所有联系人
六、后记
导入 easeui 确实有几个坑的地方,不过没多坑,Android Studio 3.3 的 add dependencies 方式感觉更加友好了。
本文源码
更多相关文章
- Android面试系列文章2018之Java部分类加载器篇
- Android防止崩溃的库,有效的降低Crash率
- Android高手进阶教程(十)之----Android(安卓)PopupWindow的使用!
- Android(安卓)使用Rtmp音视频推流
- Android中xml解析--实现软件升级功能
- [置顶] Android(安卓)WebKit消息处理
- Android(安卓)原生控件ViewFlipper实现淘宝头条垂直滚动广告条
- (原创)Android入门教程(三十六)------实现手机联系人的全选
- Android官方开发文档Training系列课程中文版:Android的安全建议