diff --git "a/AndroidNote/Android\345\237\272\347\241\200/tablayout\350\256\260\345\275\225.md" "b/AndroidNote/Android\345\237\272\347\241\200/tablayout\350\256\260\345\275\225.md" new file mode 100644 index 0000000..63a86ec --- /dev/null +++ "b/AndroidNote/Android\345\237\272\347\241\200/tablayout\350\256\260\345\275\225.md" @@ -0,0 +1,47 @@ +# TabLayout记录 + +今天用TabLayout的时候发现,TabLayout的setOnPageChangeListener的方法过期了,良好的编程习惯是不在项目中使用过时的方法的,所以要找一个替代的方案: + +``addOnPageChangeListener`` + +- setOnPageChangeListener + +``` +mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { +@Override +public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + +} + +@Override +public void onPageSelected(int position) { + +} + +@Override +public void onPageScrollStateChanged(int state) { + +} +}); +``` + +- addOnPageChangeListener + +``` +mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { +@Override +public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + +} + +@Override +public void onPageSelected(int position) { + selectedTab(position); +} + +@Override +public void onPageScrollStateChanged(int state) { + +} +}); +``` diff --git "a/AndroidNote/Android\346\212\245\351\224\231\350\256\260\345\275\225/Android\346\212\245\351\224\2312.md" "b/AndroidNote/Android\346\212\245\351\224\231\350\256\260\345\275\225/Android\346\212\245\351\224\2312.md" new file mode 100644 index 0000000..b405610 --- /dev/null +++ "b/AndroidNote/Android\346\212\245\351\224\231\350\256\260\345\275\225/Android\346\212\245\351\224\2312.md" @@ -0,0 +1,6 @@ +## Client not ready yet.. + + +这个错误导致的原因是,我们的AndroidManifest.xml中没有配置,默认的启动项。 + +还有一个原因就是可能我们的编辑器bug了,我们可以删掉再加回来。 diff --git "a/AndroidNote/Android\350\277\233\351\230\266/Android\345\206\205\345\255\230\346\263\204\346\274\217\346\200\273\347\273\223.md" "b/AndroidNote/Android\350\277\233\351\230\266/Android\345\206\205\345\255\230\346\263\204\346\274\217\346\200\273\347\273\223.md" index f1cda55..5165f3a 100644 --- "a/AndroidNote/Android\350\277\233\351\230\266/Android\345\206\205\345\255\230\346\263\204\346\274\217\346\200\273\347\273\223.md" +++ "b/AndroidNote/Android\350\277\233\351\230\266/Android\345\206\205\345\255\230\346\263\204\346\274\217\346\200\273\347\273\223.md" @@ -123,33 +123,7 @@ o = null; 在这个例子中,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身(o=null),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。 -2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用。 -例如: - -``` -public static void main(String[] args) -{ -Set set = new HashSet(); -Person p1 = new Person("唐僧","pwd1",25); -Person p2 = new Person("孙悟空","pwd2",26); -Person p3 = new Person("猪八戒","pwd3",27); -set.add(p1); -set.add(p2); -set.add(p3); -System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素! -p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变 - -set.remove(p3); //此时remove不掉,造成内存泄漏 - -set.add(p3); //重新添加,居然添加成功 -System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素! -for (Person person : set) -{ -System.out.println(person); -} -} -``` 3、监听器 diff --git "a/AndroidNote/Android\350\277\233\351\230\266/Android\351\241\271\347\233\256\346\200\273\347\273\2233.md" "b/AndroidNote/Android\350\277\233\351\230\266/Android\351\241\271\347\233\256\346\200\273\347\273\2233.md" new file mode 100644 index 0000000..73c34e7 --- /dev/null +++ "b/AndroidNote/Android\350\277\233\351\230\266/Android\351\241\271\347\233\256\346\200\273\347\273\2233.md" @@ -0,0 +1,969 @@ +# Android项目总结3 + +> 最近又独立开发了一个公司内部的小app,现在的感觉就是每一次独立开发一个新的app,都会遇到不同的问题,也都能独立解决这些问题。感觉这个状态还可以吧,同时打算把这些问题记录一下,以后再遇到同样问题的时候,有一个可以查阅的资料,也能帮助其它遇到同样问题的小伙伴们吧。 + + +- 本次项目采用的网络框架:rxjava + retrofit2,这个就不在这里总结了,有空会单独总结一篇有关文章的。 + +- 获取屏幕宽度 + +```java +public int getScreenWidth(){ + WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics dm = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(dm); + int width = dm.widthPixels; // 屏幕宽度(像素) + int height = dm.heightPixels; // 屏幕高度(像素) + float density = dm.density; // 屏幕密度(0.75 / 1.0 / 1.5) + int densityDpi = dm.densityDpi; // 屏幕密度dpi(120 / 160 / 240) + // 屏幕宽度算法:屏幕宽度(像素)/屏幕密度 + int screenWidth = (int) (width / density); // 屏幕宽度(dp) + int screenHeight = (int) (height / density);// 屏幕高度(dp) + return width; + } +``` + +- 获取屏幕高度 + +```java +public int getScreenHeight(){ + WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics dm = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(dm); + int width = dm.widthPixels; // 屏幕宽度(像素) + int height = dm.heightPixels; // 屏幕高度(像素) + float density = dm.density; // 屏幕密度(0.75 / 1.0 / 1.5) + int densityDpi = dm.densityDpi; // 屏幕密度dpi(120 / 160 / 240) + // 屏幕宽度算法:屏幕宽度(像素)/屏幕密度 + int screenWidth = (int) (width / density); // 屏幕宽度(dp) + int screenHeight = (int) (height / density);// 屏幕高度(dp) + getAndroiodScreenProperty(); + return height; + } +``` + +- 获取进程名称 + +```java +private static String getProcessName(int pid) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline")); + String processName = reader.readLine(); + if (!TextUtils.isEmpty(processName)) { + processName = processName.trim(); + } + return processName; + } catch (Throwable throwable) { + throwable.printStackTrace(); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException exception) { + exception.printStackTrace(); + } + } + return null; + } +``` + +- 不能滑动的ViewPager + +```java +public class NoScrollViewPager extends ViewPager { + private boolean noScroll = true; + + public NoScrollViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NoScrollViewPager(Context context) { + super(context); + } + + public void setNoScroll(boolean noScroll) { + this.noScroll = noScroll; + } + + @Override + public void scrollTo(int x, int y) { + super.scrollTo(x, y); + } + + @Override + public boolean onTouchEvent(MotionEvent arg0) { + if (noScroll) + return false; + else + return super.onTouchEvent(arg0); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent arg0) { + if (noScroll) + return false; + else + return super.onInterceptTouchEvent(arg0); + } + + @Override + public void setCurrentItem(int item, boolean smoothScroll) { + super.setCurrentItem(item, smoothScroll); + } + + @Override + + public void setCurrentItem(int item) { + + super.setCurrentItem(item, false);//表示切换的时候,不需要切换时间。 + + } + +} +``` + +- 自定义loadingView + +LoadingBase.java + +```java +public abstract class LoadingBase extends View { + + public LoadingBase(Context context) { + this(context, null); + } + + public LoadingBase(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LoadingBase(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + InitPaint(); + } + + public void startAnim() { + stopAnim(); + startViewAnim(0f, 1f, 500); + } + + public void startAnim(int time) { + stopAnim(); + startViewAnim(0f, 1f, time); + } + + + public void stopAnim() { + if (valueAnimator != null) { + clearAnimation(); + + valueAnimator.setRepeatCount(0); + valueAnimator.cancel(); + valueAnimator.end(); + if (OnStopAnim() == 0) { + valueAnimator.setRepeatCount(0); + valueAnimator.cancel(); + valueAnimator.end(); + } + + } + } + + public ValueAnimator valueAnimator; + + private ValueAnimator startViewAnim(float startF, final float endF, long time) { + valueAnimator = ValueAnimator.ofFloat(startF, endF); + valueAnimator.setDuration(time); + valueAnimator.setInterpolator(new LinearInterpolator()); + + + valueAnimator.setRepeatCount(SetAnimRepeatCount()); + + + + if (ValueAnimator.RESTART == SetAnimRepeatMode()) { + valueAnimator.setRepeatMode(ValueAnimator.RESTART); + + } else if (ValueAnimator.REVERSE == SetAnimRepeatMode()) { + valueAnimator.setRepeatMode(ValueAnimator.REVERSE); + + } + + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + OnAnimationUpdate(valueAnimator); + + } + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + + } + + @Override + public void onAnimationStart(Animator animation) { + + super.onAnimationStart(animation); + } + + @Override + public void onAnimationRepeat(Animator animation) { + super.onAnimationRepeat(animation); + OnAnimationRepeat(animation); + } + }); + if (!valueAnimator.isRunning()) { + AinmIsRunning(); + valueAnimator.start(); + + } + + return valueAnimator; + } + + + protected abstract void InitPaint(); + + protected abstract void OnAnimationUpdate(ValueAnimator valueAnimator); + + protected abstract void OnAnimationRepeat(Animator animation); + + protected abstract int OnStopAnim(); + + protected abstract int SetAnimRepeatMode(); + + protected abstract int SetAnimRepeatCount(); + + protected abstract void AinmIsRunning(); + + + public int dip2px(float dpValue) { + final float scale = getContext().getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public float getFontlength(Paint paint, String str) { + Rect rect = new Rect(); + paint.getTextBounds(str, 0, str.length(), rect); + return rect.width(); + } + + public float getFontHeight(Paint paint, String str) { + Rect rect = new Rect(); + paint.getTextBounds(str, 0, str.length(), rect); + return rect.height(); + + } + + public float getFontHeight(Paint paint) { + Paint.FontMetrics fm = paint.getFontMetrics(); + return fm.descent - fm.ascent; + } + +} + +``` + +LoadingView.java + +```java +public class LoadingView extends LoadingBase { + + private Paint mPaint; + private Paint mPaintPro; + + private float mWidth = 0f; + private float mPadding = 0f; + private float startAngle = 0f; + RectF rectF = new RectF(); + + public LoadingView(Context context) { + super(context); + } + + public LoadingView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (getMeasuredWidth() > getHeight()) + mWidth = getMeasuredHeight(); + else + mWidth = getMeasuredWidth(); + mPadding = 5; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2 - mPadding, mPaintPro); + rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding); + canvas.drawArc(rectF, startAngle, 100 + , false, mPaint);//第四个参数是否显示半径 + + } + + + private void initPaint() { + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(Color.WHITE); + mPaint.setStrokeWidth(8); + + mPaintPro = new Paint(); + mPaintPro.setAntiAlias(true); + mPaintPro.setStyle(Paint.Style.STROKE); + mPaintPro.setColor(Color.argb(100, 255, 255, 255)); + mPaintPro.setStrokeWidth(8); + + } + + + public void setViewColor(int color) { + mPaintPro.setColor(color); + postInvalidate(); + } + + public void setBarColor(int color) { + mPaint.setColor(color); + postInvalidate(); + } + + + @Override + protected void InitPaint() { + initPaint(); + } + + @Override + protected void OnAnimationUpdate(ValueAnimator valueAnimator) { + float value = (float) valueAnimator.getAnimatedValue(); + startAngle = 360 * value; + + invalidate(); + } + + @Override + protected void OnAnimationRepeat(Animator animation) { + + } + + @Override + protected int OnStopAnim() { + return 0; + } + + @Override + protected int SetAnimRepeatMode() { + return ValueAnimator.RESTART; + } + + @Override + protected void AinmIsRunning() { + + } + + @Override + protected int SetAnimRepeatCount() { + return ValueAnimator.INFINITE; + } + +} + +``` + + +- 自定义异常 + +```java +public class NetworkException extends RuntimeException { + + + + public static final int REQUEST_OK = 100; + public static final int REQUEST_FAIL = 101; + public static final int METHOD_NOT_ALLOWED = 102; + public static final int PARAMETER_ERROR = 103; + public static final int UID_OR_PWD_ERROR = 104; + public static final int SERVER_INTERNAL_ERROR = 105; + public static final int REQUEST_TIMEOUT = 106; + public static final int CONNECTION_ERROR = 107; + public static final int VERIFY_EXPIRED = 108; + public static final int NO_DATA = 109; + + + public NetworkException(int resultCode) { + this(getNetworkExceptionMessage(resultCode)); + } + + public NetworkException(String detailMessage) { + super(detailMessage); + } + + /** + * 将结果码转换成对应的文本信息 + */ + private static String getNetworkExceptionMessage(int code) { + String message = ""; + switch (code) { + case REQUEST_OK: + message = "请求成功"; + break; + case REQUEST_FAIL: + message = "请求失败"; + break; + + case METHOD_NOT_ALLOWED: + message = "请求方式不允许"; + break; + case PARAMETER_ERROR: + message = "用户不存在"; + break; + case UID_OR_PWD_ERROR: + message = "用户名或密码错误"; + break; + case SERVER_INTERNAL_ERROR: + message = "服务器内部错误"; + break; + case REQUEST_TIMEOUT: + message = "请求超时"; + break; + case CONNECTION_ERROR: + message = "连接错误"; + break; + case VERIFY_EXPIRED: + message = "验证过期"; + break; + case NO_DATA: + message = "没有数据"; + break; + case 110: + message = "该用户已存在"; + break; + default: + message = "未知错误"; + } + return message; + } + +} + +``` + +- 自定义OnClickListener防止重复点击发生的问题 + +```java +public abstract class NoDoubleClickListener implements View.OnClickListener { + + private static final int MIN_CLICK_DELAY_TIME = 1000; + private long lastClickTime = 0; + + @Override + public void onClick(View v) { + long time = System.currentTimeMillis(); + if (time - lastClickTime > MIN_CLICK_DELAY_TIME) { + lastClickTime = time; + onNoDoubleClick(v); + } + + } + public abstract void onNoDoubleClick(View v); +} +``` + +- 创建notification并且添加点击事件 + +```java +PendingIntent mainPendingIntent = null; + +Intent mainIntent = new Intent(this, MainActivity.class); +mainPendingIntent = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + + NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this); + mBuilder.setContentTitle("您有新的订单,请登陆app查看")//设置通知栏标题 + .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图 + // .setNumber(number) //设置通知集合的数量 + .setTicker("您有新的订单,请登陆app查看") //通知首次出现在通知栏,带上升动画效果的 + .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间 + .setContentIntent(mainPendingIntent) + .setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级 + .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消 + .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接) + .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合 + //Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission + .setSmallIcon(R.mipmap.ic_launcher);//设置通知小ICON + + + mNotificationManager.notify(1, mBuilder.build()); +``` + + +- 获取Imei,兼容Android N(easypermission) + +```java +public class LoginActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks { + + private String imei = "00000000000"; + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + String[] perms = {android.Manifest.permission.READ_PHONE_STATE}; + if (EasyPermissions.hasPermissions(this, perms)) { + Log.e("lin", "---lin---> imie if"); + TelephonyManager telephonyManager = (TelephonyManager) this.getApplicationContext().getSystemService(this.getApplicationContext().TELEPHONY_SERVICE); + imei = telephonyManager.getDeviceId(); + } else { + Log.e("lin", "---lin---> imie else"); + EasyPermissions.requestPermissions(this, "正在申请获取手机唯一编码", + 100, perms); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // Forward results to EasyPermissions + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); + } + + @Override + public void onPermissionsGranted(int requestCode, List perms) { + TelephonyManager telephonyManager = (TelephonyManager) this.getApplicationContext().getSystemService(this.getApplicationContext().TELEPHONY_SERVICE); + imei = telephonyManager.getDeviceId(); + } + + @Override + public void onPermissionsDenied(int requestCode, List perms) { + + } + +} +``` + +- 倒计时控件 + +```java +private TimeCount mTimeCount; +mTimeCount = new TimeCount(60000, 1000); +mTimeCount.start(); + +class TimeCount extends CountDownTimer { + public TimeCount(long millisInFuture, long countDownInterval) { + super(millisInFuture + 200 , countDownInterval); + } + + @Override + public void onFinish() {// 计时完毕 + tvGetCodeActivityLogin.setText("获取验证码"); + tvGetCodeActivityLogin.setClickable(true); + } + + + @Override + public void onTick(long millisUntilFinished) {// 计时过程 + long time = millisUntilFinished / 1000; + String timeString = String.valueOf(time); + timeString = timeString + "S"; + tvGetCodeActivityLogin.setText(timeString); + tvGetCodeActivityLogin.setClickable(false);//防止重复点击 + } +} +``` + +- 设置editext hint字的颜色值 + +```java +CharSequence hint = edtAuthCodeActivityLogin.getHint(); +SpannableString ss = new SpannableString(hint); +AbsoluteSizeSpan ass = new AbsoluteSizeSpan(17, true); +edtAuthCodeActivityLogin.setHintTextColor(0xffdddddd); +ss.setSpan(ass, 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); +edtAuthCodeActivityLogin.setHint(new SpannedString("短信验证码")); +``` + +- App内采用的开源相册 Album + +- 图片压缩框架 Luban + +- fragment类似activity的onResume() +```java +protected boolean isCreate = false; + +@Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (isVisibleToUser && isCreate) { + getOrderList("-1"); + } + } +``` + +- 利用反射设置tablayout下划线长度 + +```java +tabLayoutOrderFragment.post(new Runnable() { + @Override + public void run() { + setIndicator(tabLayoutOrderFragment,20,20); + } + }); + +public void setIndicator(TabLayout tabs, int leftDip, int rightDip) { + Class tabLayout = tabs.getClass(); + Field tabStrip = null; + try { + tabStrip = tabLayout.getDeclaredField("mTabStrip"); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + + tabStrip.setAccessible(true); + LinearLayout llTab = null; + try { + llTab = (LinearLayout) tabStrip.get(tabs); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + int left = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, leftDip, Resources.getSystem().getDisplayMetrics()); + int right = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightDip, Resources.getSystem().getDisplayMetrics()); + + for (int i = 0; i < llTab.getChildCount(); i++) { + View child = llTab.getChildAt(i); + child.setPadding(0, 0, 0, 0); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1); + params.leftMargin = left; + params.rightMargin = right; + child.setLayoutParams(params); + child.invalidate(); + } + + } +``` + +- 封装一个链式调用的dialog + +```java +public class DialogUtils { + + private Context mContext; + private AlertDialog mAlertDialog; + private DialogUtils.Builder mBuilder; + private boolean mHasShow = false; + private String mTitle; + private String mMessage; + private int messageColor = -1; + private SingleButtonCallback mPositiveCallback; + private SingleButtonCallback mNegativeCallback = new SingleButtonCallback() { + @Override + public void onClick(@NonNull DialogUtils dialog, View.OnClickListener listener) { + dismiss(); + } + }; + private boolean mCancel; + + + public DialogUtils(Context context) { + this.mContext = context; + } + + public void show() { + if (!mHasShow) { + mBuilder = new Builder(); + } else { + mAlertDialog.show(); + } + mHasShow = true; + } + + public void dismiss() { + mAlertDialog.dismiss(); + } + + public DialogUtils setTitle(String title) { + this.mTitle = title; + if (mBuilder != null) { + mBuilder.setTitle(title); + } + return this; + } + + public DialogUtils setMessage(String message) { + this.mMessage = message; + if (mBuilder != null) { + mBuilder.setMessage(message); + } + return this; + } + + public DialogUtils setMessageColor(int color) { + this.messageColor = color; + return this; + } + + public DialogUtils setCanceledOnTouchOutside(boolean cancel) { + this.mCancel = cancel; + if (mBuilder != null) { + mBuilder.setCanceledOnTouchOutside(mCancel); + } + return this; + } + + public DialogUtils setPositive(SingleButtonCallback positiveCallback) { + this.mPositiveCallback = positiveCallback; + return this; + } + + public DialogUtils setNegative(SingleButtonCallback negativeCallback) { + this.mNegativeCallback = negativeCallback; + return this; + } + + + public enum DialogAction { + POSITIVE, + NEGATIVE + } + + public interface SingleButtonCallback { + void onClick(@NonNull DialogUtils dialog, View.OnClickListener listener); + } + + + private class Builder { + + private TextView mTitleView; + private TextView mMessageView; + private TextView mPositive, mNegative; + private Window mAlertDialogWindow; + private RelativeLayout mDialog; + + private Builder() { + mAlertDialog = new AlertDialog.Builder(mContext, R.style.Theme_AppCompat_Dialog).create(); + mAlertDialog.show(); + mAlertDialog.getWindow() + .clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + mAlertDialog.getWindow() + .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE); + + mAlertDialogWindow = mAlertDialog.getWindow(); + mAlertDialogWindow.setBackgroundDrawable( + new ColorDrawable(android.graphics.Color.TRANSPARENT)); + View contentView = LayoutInflater.from(mContext) + .inflate(R.layout.dialog_util_layout, null); + contentView.setFocusable(true); + contentView.setFocusableInTouchMode(true); + mAlertDialogWindow.setBackgroundDrawableResource(R.drawable.material_dialog_window); + mAlertDialogWindow.setContentView(contentView); + mTitleView = (TextView) mAlertDialogWindow.findViewById(R.id.tv_title); + mMessageView = (TextView) mAlertDialogWindow.findViewById(R.id.tv_hint); + mPositive = (TextView) mAlertDialogWindow.findViewById(R.id.tv_sure); + mNegative = (TextView) mAlertDialogWindow.findViewById(R.id.tv_cancel); + mDialog = (RelativeLayout) mAlertDialogWindow.findViewById(R.id.dialog); + Log.i("lin", "----lin----> 宽 " + MyApplication.get().getScreenWidth()); + Log.i("lin", "----lin----> 高 " + MyApplication.get().getScreenHeight()); + + mDialog.setLayoutParams(new RelativeLayout.LayoutParams(MyApplication.get().getScreenWidth() / 4 * 3, MyApplication.get().getScreenHeight() / 4)); + if (mTitle != null) { + mTitleView.setText(mTitle); + } + if (mMessage != null) { + mMessageView.setText(mMessage); + } + if (messageColor != -1) { + mMessageView.setTextColor(messageColor); + } + mAlertDialog.setCanceledOnTouchOutside(mCancel); + mAlertDialog.setCancelable(mCancel); + if (mPositiveCallback != null) { + mPositive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mPositiveCallback.onClick(DialogUtils.this, this); + } + }); + } + if (mNegativeCallback != null) { + mNegative.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mNegativeCallback.onClick(DialogUtils.this, this); + } + }); + } + + } + + + public void setTitle(String title) { + mTitleView.setText(title); + } + + public void setMessage(String message) { + if (mMessageView != null) { + mMessageView.setText(message); + } + } + + public void setCanceledOnTouchOutside(boolean canceledOnTouchOutside) { + mAlertDialog.setCanceledOnTouchOutside(canceledOnTouchOutside); + mAlertDialog.setCancelable(canceledOnTouchOutside); + } + + } + +} +``` + +- 将loadingView封装成loadingViewDialog + +```java +public class LoadingUtils { + + + private Context mContext; + private AlertDialog mAlertDialog; + private LoadingUtils.Builder mBuilder; + private boolean mHasShow = false; + private String mMessage; + private boolean mCancel; + + + public LoadingUtils(Context context) { + this.mContext = context; + } + + public void show() { + if (!mHasShow) { + mBuilder = new LoadingUtils.Builder(); + } else { + mAlertDialog.show(); + } + mHasShow = true; + } + + public void dismiss() { + mAlertDialog.dismiss(); + } + + + public LoadingUtils setMessage(String message) { + this.mMessage = message; + if (mBuilder != null) { + mBuilder.setMessage(message); + } + return this; + } + + public LoadingUtils setCanceledOnTouchOutside(boolean cancel) { + this.mCancel = cancel; + if (mBuilder != null) { + mBuilder.setCanceledOnTouchOutside(mCancel); + } + return this; + } + + private class Builder { + + private Window mAlertDialogWindow; + private TextView mMessageView; + private RelativeLayout mDialog; + private LoadingView loadingView; + + private Builder() { + mAlertDialog = new AlertDialog.Builder(mContext, R.style.Theme_AppCompat_Dialog).create(); + mAlertDialog.show(); + mAlertDialog.getWindow() + .clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + mAlertDialog.getWindow() + .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE); + + mAlertDialogWindow = mAlertDialog.getWindow(); + mAlertDialogWindow.setBackgroundDrawable( + new ColorDrawable(android.graphics.Color.TRANSPARENT)); + View contentView = LayoutInflater.from(mContext) + .inflate(R.layout.loading_view_layout, null); + contentView.setFocusable(true); + contentView.setFocusableInTouchMode(true); + mAlertDialogWindow.setBackgroundDrawableResource(R.drawable.material_dialog_window); + mAlertDialogWindow.setContentView(contentView); + mDialog = (RelativeLayout) contentView.findViewById(R.id.rl_loading_view); + mMessageView = (TextView) contentView.findViewById(R.id.tv_hint); + loadingView = (LoadingView) contentView.findViewById(R.id.loading_view); + + loadingView.setViewColor(Color.argb(100, 255, 255, 255)); + loadingView.startAnim(); + loadingView.setBarColor(0xFF42a5f5); + loadingView.startAnim(); + + + Log.i("lin", "----lin----> 宽 " + MyApplication.get().getScreenWidth()); + Log.i("lin", "----lin----> 高 " + MyApplication.get().getScreenHeight()); + + mDialog.setLayoutParams(new FrameLayout.LayoutParams(MyApplication.get().getScreenWidth() / 4 * 3, MyApplication.get().getScreenHeight() / 4)); + + if (mMessage != null) { + mMessageView.setText(mMessage); + } + mAlertDialog.setCanceledOnTouchOutside(mCancel); + mAlertDialog.setCancelable(mCancel); + } + + + public void setMessage(String message) { + if (mMessageView != null) { + mMessageView.setText(message); + } + } + + public void setCanceledOnTouchOutside(boolean canceledOnTouchOutside) { + mAlertDialog.setCanceledOnTouchOutside(canceledOnTouchOutside); + mAlertDialog.setCancelable(canceledOnTouchOutside); + } + + } + +} + +``` + +- 判断当前app是否有网络 +```java +public static boolean isNetworkAvailable(Context context) { + ConnectivityManager cm = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + if (cm == null) { + + } else { + NetworkInfo[] info = cm.getAllNetworkInfo(); + if (info != null) { + for (int i = 0; i < info.length; i++) { + if (info[i].getState() == NetworkInfo.State.CONNECTED) { + return true; + } + } + } + } + return false; + } +``` diff --git "a/AndroidNote/Android\350\277\233\351\230\266/\346\243\200\346\237\245app\346\230\257\345\220\246\346\234\211\346\216\250\351\200\201\346\235\203\351\231\220.md" "b/AndroidNote/Android\350\277\233\351\230\266/\346\243\200\346\237\245app\346\230\257\345\220\246\346\234\211\346\216\250\351\200\201\346\235\203\351\231\220.md" new file mode 100644 index 0000000..968dd23 --- /dev/null +++ "b/AndroidNote/Android\350\277\233\351\230\266/\346\243\200\346\237\245app\346\230\257\345\220\246\346\234\211\346\216\250\351\200\201\346\235\203\351\231\220.md" @@ -0,0 +1,53 @@ +# 检查app是否具有推送权限 + +检查是否有推送权限 + +```java +@RequiresApi(api = Build.VERSION_CODES.KITKAT) + private boolean isNotificationEnabled(Context context) { + + String CHECK_OP_NO_THROW = "checkOpNoThrow"; + String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; + + AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + ApplicationInfo appInfo = context.getApplicationInfo(); + String pkg = context.getApplicationContext().getPackageName(); + int uid = appInfo.uid; + + Class appOpsClass = null; + /* Context.APP_OPS_MANAGER */ + try { + appOpsClass = Class.forName(AppOpsManager.class.getName()); + Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, + String.class); + Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION); + + int value = (Integer) opPostNotificationValue.get(Integer.class); + return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED); + + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +``` + + +跳转到登录页面 + +```java + +private void toSetting() { + Intent localIntent = new Intent(); + localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (Build.VERSION.SDK_INT >= 9) { + localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + localIntent.setData(Uri.fromParts("package", getPackageName(), null)); + } else if (Build.VERSION.SDK_INT <= 8) { + localIntent.setAction(Intent.ACTION_VIEW); + localIntent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails"); + localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName()); + } + startActivity(localIntent); +} +``` diff --git "a/JavaNote/Javaee/Spring-boot\345\205\245\351\227\250.md" "b/JavaNote/Javaee/Spring-boot\345\205\245\351\227\250.md" new file mode 100644 index 0000000..ba69c50 --- /dev/null +++ "b/JavaNote/Javaee/Spring-boot\345\205\245\351\227\250.md" @@ -0,0 +1,23 @@ +# Spiring-boot入门 + +> 最入门级别的Spring框架,省了很多配置的过程,比较适合初学者,或者微站的使用,所以打算从这里开始学起。 + +学习的地址:https://www.bysocket.com/?p=1627 + +包目录结构: + +![](https://ws1.sinaimg.cn/large/006tNc79ly1flcxydakxlj30km0zyt9d.jpg) + +这里面分了很多层,主要有的层就是,controller,dao,domain,service,Application。 + +SpringBoot的优势就在于它内置了TomCat,并且不需要过多的配置就可以直接使用,也不用配置Spring那一套。 + +这次实现的是springboot-restful的过程,rest是web的一种框架风格,主要用于前后端分离。 + +首先我们要实现一个controller,这里面负责拦截用户的url,然后可以设置是GET还是POST的方法,同时我们还需要实现一个dao层,这层是用来负责数据持久化的,简单说就是这里和数据进行的直接接触。接下来要实现一个domain层,这一层大家应该都能明白有的时候我们叫它model有的时候叫它bean,接下来就要实现service层了,这一层中我们需要定义一个接口,并且编写一个它的实现类就可以了,我们在controller中调用的便是这一层的内容。 + +其实我们的代码,以上便已经编写完成了,但是我们还有几个点是没有完成的,我们的sql语句还没有配置呢。我们需要在resources/mapper/CityMapper.xml内中写入。在resources/application.properties中写入数据库的信息,配置一下就好。 + +代码地址:https://github.com/linsir6/Javaee-Spring-demo + +博主在Spring方面也是刚刚起步的小菜鸟,写以上内容只是为了,记录学习笔记,要是有什么错误,欢迎大家提出指正。 diff --git "a/JavaNote/Java\347\233\270\345\205\263/Java\345\210\251\347\224\250listener\345\256\236\347\216\260\345\233\236\350\260\203\357\274\214\345\215\263\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217.md" "b/JavaNote/Java\347\233\270\345\205\263/Java\345\210\251\347\224\250listener\345\256\236\347\216\260\345\233\236\350\260\203\357\274\214\345\215\263\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217.md" index ca3f979..ee308e2 100644 --- "a/JavaNote/Java\347\233\270\345\205\263/Java\345\210\251\347\224\250listener\345\256\236\347\216\260\345\233\236\350\260\203\357\274\214\345\215\263\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217.md" +++ "b/JavaNote/Java\347\233\270\345\205\263/Java\345\210\251\347\224\250listener\345\256\236\347\216\260\345\233\236\350\260\203\357\274\214\345\215\263\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217.md" @@ -1,13 +1,15 @@ > java中实现观察者模式有很多种方式,上一篇文章介绍到了,[利用callback的方式实现了回调](http://www.jianshu.com/p/67190bdce647),这篇文章准备介绍的是利用listener实现回调。 ---- -#Java回调机制 +# Java回调机制 + +#### 根据实时性划分: -####根据实时性划分: - [同步回调](http://www.jianshu.com/p/67190bdce647) - [异步回调](http://www.jianshu.com/p/67190bdce647) -####实现方式 +#### 实现方式 + * [利用匿名内部类即callbck来实现](http://www.jianshu.com/p/67190bdce647) * 用listener来实现 @@ -44,10 +46,11 @@ public class Test extends AppCompatActivity implements View.OnClickListener { ```` -####回调的本质 +#### 回调的本质 + 其实无论哪种方式来实现回调,利用的思想都是观察者模式,即在我们选择订阅之后,当对方做出任何举动的时候会给我们发送一条信息,这样做的好处是省着我们用一个新的线程轮训检测对方的状态,可以节省很多的资源。 -####应用的场景 +#### 应用的场景 - 如果我们需要将信息一层一层的返回去的时候,正如我下面的例子,那么可能用listener更为适合我们,因为我们可以将这个listener进行传递,在需要查看数据的时候进行回调它。或者当我们有很多事件需要回调的时候,可以实现一个listener然后发送不同的信息,进行区分。这样代码看起来会简洁一些,不会像callback一样,会嵌套很多层,也不会写出很多个callback来。 diff --git "a/Linux/Android-GitLabCi\351\205\215\347\275\256.md" "b/Linux/Android-GitLabCi\351\205\215\347\275\256.md" new file mode 100644 index 0000000..d3f4c5b --- /dev/null +++ "b/Linux/Android-GitLabCi\351\205\215\347\275\256.md" @@ -0,0 +1,243 @@ +# Android GitLabCi的配置 + +> 近期接到了这样一个小任务,由于在测试过程中我们需要经常打包apk,这个任务量说大不大说小也不小吧,然后决定配置一个ci,让 gitlab自动来做这件事情。 + +## 服务器环境 + +服务器:centos7 + +## 任务列表 + +- 安装open JDK +- 安装sdk +- 安装gradle +- 安装gitlab-ci-multi-runner + +### 安装jdk + +``` +yum search java-1.8.0 +yum install java-1.8.0-openjdk.x86_64 +``` + +查看安装结果: +``` +# java -version + +openjdk version "1.8.0_131" +OpenJDK Runtime Environment (build 1.8.0_131-b12) +OpenJDK 64-Bit Server VM (build 25.131-b12, mixed mode) +``` + +配置环境变量: +在当前用户的bash_profile或者/etc/profile配置一下环境变量和JAVA_HOME +eg: +``` +export JAVA_HOME=/opt/soft/java +export CLASSPATH=:/lib:/jre/lib +export PATH=$PATH:$JAVA_HOME/bin +``` +``source profile`` 让配置立即生效 + + +### 安装sdk + +现在sdk都是由apkmanager统一管理的 + +下载sdktools + +``` +cd /opt + +mkdir androidSdk + +wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip + +unzip sdk-tools-linux-3859397.zip +``` + +在/opt/androidsdk/tools/bin下面有sdkmanager的命令,我们可以利用``sdkmanager --list``查看我们已经安装的内容,还可以用例如这样的命令``sdkmanager build-tools;19.1.0``,来安装我们需要的内容。装完之后我们还是要习惯性的``sdkmanager --licenses``看一下有没有没有同意的licenses. + +配置sdk环境变量 + +``` +PATH=$PATH:/opt/androidsdk/tools/bin +PATH=$PATH:/opt/androidsdk/platform-tools +export PATH +export ANDROID_HOME=/opt/androidsdk +export ANDROID_NDK_HOME=/opt/androidsdk/ndk-bundle +``` +``source profile`` 让配置立即生效 + + +运行``adb version`` + +``` +Android Debug Bridge version 1.0.39 +Revision 3db08f2c6889-android +Installed as /opt/androidsdk/platform-tools/adb +``` + +如果看到以上内容,就代表成功了。 + + + + +### 安装gradle + +创建gradle目录解压并安装 + +``` +cd /opt +mkdir gradle +cd gradle +wget https://services.gradle.org/distributions/gradle-4.0.1-bin.zip +unzip gradle-4.0.1-bin.zip +``` + +配置环境变量(可以在/etc/profile下面配置,也可以在用户的bash_profile下面配置) +``` +PATH=$PATH:/opt/androidsdk/tools/bin +PATH=$PATH:/opt/gradle/gradle-4.0.1/bin +PATH=$PATH:/opt/androidsdk/platform-tools +export PATH + +export ANDROID_HOME=/opt/androidsdk +export ANDROID_NDK_HOME=/opt/androidsdk/ndk-bundle +``` + +检查是否配置成功 +``` +gradle -version +``` + +### 安装gitlab-ci-multi-runner + +``` +yum install gitlab-ci-multi-runner +``` + +注册runner: + +``` +$ sudo gitlab-ci-multi-runner register + +Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com ) +https://mygitlab.com/ci +Please enter the gitlab-ci token for this runner +xxx-xxx-xxx +Please enter the gitlab-ci description for this runner +my-runner +INFO[0034] fcf5c619 Registering runner... succeeded +Please enter the executor: shell, docker, docker-ssh, ssh? +docker +Please enter the Docker image (eg. ruby:2.1): +node:4.5.0 +INFO[0037] Runner registered successfully. Feel free to start it, but if it's +running already the config should be automatically reloaded! +``` + +更新runner + +``` + # For Debian/Ubuntu + sudo apt-get update + sudo apt-get install gitlab-ci-multi-runner + + # For CentOS + sudo yum update + sudo yum install gitlab-ci-multi-runner +``` + +配置文件默认会在``/etc/gitlab-runner/config.toml`` + +简单配置: +``` +concurrent = 1 +check_interval = 0 +``` + +示例配置文件: +``` +[[runners]] + name = "test" + url = "http://your-domain.com/ci" + token = "your-token" + executor = "docker" + [runners.docker] + tls_verify = false + image = "node:4.5.0" + privileged = false + disable_cache = false + volumes = ["/cache"] + [runners.cache] + [runners.kubernetes] + host = "" + cert_file = "" + key_file = "" + ca_file = "" + image = "" + namespace = "" + privileged = false + cpus = "" + memory = "" + service_cpus = "" + service_memory = "" +``` + + +## 配置构建任务 + +在项目的根目录下,添加``.gitlab-ci.yml``文件, + +``` +image: openjdk:8-jdk +stages: + - build + - test +build: + tags: + - test-env + stage: build + script: + - ./gradlew assembleDebug + artifacts: + paths: + - app/build/outputs/ +``` + +这个就是简单的,build一下打包出一个debug版本的apk,当然我们还可以编辑出各种脚本,我们也可以在这里编写代码的测试的,这个都是可以的。 + +这里面有一个``tags:- test-env``的概念,这个就是决定我们要选择哪一个runner。 + + + +效果图: + +![](https://ws2.sinaimg.cn/large/006tKfTcly1fl9do2q6fvj31kw0ewgmn.jpg) + +![](https://ws3.sinaimg.cn/large/006tKfTcly1fl9dopk7rlj31kw1ien66.jpg) + +---- + +最后补充一下,我们本次是在centos7上面配置嘛,遇到了很多权限方面的问题,简单补充一下: + + +![权限](https://ws3.sinaimg.cn/large/006tKfTcly1fl9ddsdj99j30sa09ot9t.jpg) + +这里面是分成9位的,前三位是自己的权限,中间三位是同组人的权限,最后三位是其它人的权限。 +我们可以根据自己的实际情况进行分配权限,最大就是``chmod -777 xxx``这个就是最高级别的权限了,就是谁都可以动的了,大家要合理决定是否给出这么大权限,当然如果这是一个文件夹我们还可以加一个``-r``参数,这样就会递归分配它下面的目录。 + +我们也可以通过``chown -R --recursive``把文件夹的所有者分配给别人。 + + +### 参考 + +- [使用Gitlab搭建Android和iOS的持续集成和持续发布环境(二)](http://www.jianshu.com/p/a9bb0dd8d284) +- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) +- [劈荆斩棘:Gitlab 部署 CI 持续集成](http://www.cnblogs.com/xishuai/p/gitlab-ci.html) +- [GitLab-CI安装教程](http://blog.csdn.net/u013096666/article/details/76521426) +- [centos7中安装JDK](http://blog.devwiki.net/index.php/2017/07/20/centos-install-jdk.html) +- [centos7中安装Android SDK](http://blog.devwiki.net/index.php/2017/07/20/centos-install-android-sdk.html) +- [centos7中安装gradle](http://blog.devwiki.net/index.php/2017/07/20/centos-install-gradle.html) +- [gradle build提示You have not accepted the license agreements of the following SDK components](http://blog.csdn.net/xlyrh/article/details/54667633) diff --git a/README.md b/README.md index 345b7a4..284538d 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,9 @@ **Android-Note里面记录了有关Android的常用基础知识、面试中经常被问到的知识点、Android进阶必备的知识。** -Android-Note同时是博主的一个学习笔记,之前博主尝试着维护多个学习笔记的项目,但是由于项目过多,不易于维护,故准备,暂时将一些其它内容的笔记也记录在这个项目中。 - -博主目前从事的工作是Android开发,但是对IOS开发,Java,Python,Node,Go,Vim,网络协议有着较为浓厚的兴趣,所以也会将一些学习的笔记记录在这里,欢迎大家和我一起讨论。 +由于目前正处在春招的时间点上,博主也正在积极准备春招,在这里把一些面试常问到的知识点总结一下。 +专门为面试准备的面试内容: https://github.com/linsir6/Android-Advance ## Android @@ -33,11 +32,15 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [RecyclerView的使用](/AndroidNote/Android基础/RecyclerView的简介.md) - [Android获取SHA1](/AndroidNote/Android基础/Android获取SHA1.md) - [Recyclerview和Listview的异同.md](/AndroidNote/Android进阶/Recyclerview和Listview的异同.md) -- [初识ConstraintLayout](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649548068&idx=1&sn=f750ae79c9458f89c3cf85f7573ba579&scene=21#wechat_redirect) +- [TabLayout记录](/AndroidNote/Android基础/tablayout记录.md) + + ### Android进阶 -- [Android官网建议代码规范.md](https://source.android.com/source/code-style#java-language-rules) +- [Android 触控事件解析 - Mastering The Android Touch System 笔记](https://www.jianshu.com/p/c65da5e81afd) +- [Android 多进程使用场景](http://blog.csdn.net/qq_27489007/article/details/54377655) +- [Android官网建议代码规范](https://source.android.com/source/code-style#java-language-rules) - [Android中的动画](/AndroidNote/Android进阶/Android中的动画.md) - [深入了解MVXX模式](/AndroidNote/Android进阶/深入了解MVXX模式.md) - [Android项目总结](/AndroidNote/Android进阶/Android项目总结.md) @@ -49,7 +52,6 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [Handler引起的内存泄漏的案例与分析](/AndroidNote/Android进阶/Handler引起的内存泄漏以及分析.md) - [Android性能优化.md](/AndroidNote/Android进阶/Android性能优化.md) - [LeakCanary的工作过程以及原理](AndroidNote/Android性能优化相关/LeakCanary工作过程以及原理.md) -- [AIDL的具体实现流程](http://bbs.51cto.com/thread-1086040-1.html) - [Android中利用异步来优化处理速度](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=401555104&idx=1&sn=501e6158e6eb26b4e86467be01fd290e&scene=21#wechat_redirect) - [为什么选择Binder实现Android中跨进程通信](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649548116&idx=1&sn=d11a131871623110c74e3676d4fcf785&chksm=f1180e29c66f873f9cac5dc104f97fae319c1831219a9fd9458a4429f16562f6712cc7f65a4c&scene=21#wechat_redirect) - [三大图片缓存框架的对比](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649547344&idx=2&sn=e3fa99b52055a37202634fe61a62d439&scene=21#wechat_redirect) @@ -60,6 +62,52 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [插件化实现的思想](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649547660&idx=1&sn=d2764b282fdf1c1fdb629f9c2ca9b10f&scene=21#wechat_redirect) - [Android 7.0新特性总结](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=2649548427&idx=1&sn=df9956d131a6da5f29292cd05a61b16e&chksm=f1180df6c66f84e0097eea33bba6abb125b6bcd6847720a7c481a85001a52ae2e4b1941690eb&scene=21#wechat_redirect) - [RecyclerView局部刷新的坑](http://blog.csdn.net/jdsjlzx/article/details/52893469) +- [Android单元测试](https://tech.meituan.com/Android_unit_test.html) +- [gradle 详解——你真的了解Gradle吗?](http://blog.csdn.net/u013132758/article/details/52355915) +- [AndroidStudio-Gradle多渠道打包](http://stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/) +- [Android基础入门教程——8.1.1 Android中的13种Drawable小结 Part 1](http://blog.csdn.net/coder_pig/article/details/49006217) +- [Android基础入门教程——8.1.2 Android中的13种Drawable小结 Part 2](http://blog.csdn.net/coder_pig/article/details/49008397) +- [Android-Drawable高级用法](http://blog.csdn.net/lmj623565791/article/details/43752383) +- [Android 4.4 中 WebView 使用注意事项](https://github.com/cundong/blog/blob/master/Android%204.4%20%E4%B8%AD%20WebView%20%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.md) +- [Android图像处理 - 高斯模糊的原理及实现](https://mp.weixin.qq.com/s?__biz=MzI2MTU3MTE4NQ==&mid=2247483896&idx=1&sn=50c61e2c78aa610a1944be6a89bd75e5&chksm=ea5916e6dd2e9ff0a62af64c7f345ffb5c6dafdb65847b757b99afcc6fed8e1270e915dbcb25&mpshare=1&scene=23&srcid=1001DxwdQpiMwea74mczpSw8#rd) +- [Android实战——GreenDao3.2的使用,爱不释手](https://mp.weixin.qq.com/s/4Nx2DacsK65O5LanPZUszA) +- [Realm for Android详细教程](http://www.jianshu.com/p/28912c2f31db#) +- [给 Android 开发者的 RxJava 详解](http://gank.io/post/560e15be2dca930e00da1083) +- [Android 谈谈自动化测试](https://mp.weixin.qq.com/s/-0e1wd2iveQPMWgGFcmOwQ) +- [检查app是否具有通知栏权限](/AndroidNote/Android进阶/检查app是否有推送权限.md) +- [Android中图片压缩分析(上)](https://mp.weixin.qq.com/s/QZ-XTsO7WnNvpnbr3DWQmg) +- [Android Studio3.0更新之路(遇坑必入)](http://www.jianshu.com/p/15afb8234d19) +- [Android Studio3.0正式版填坑路](http://www.jianshu.com/p/9b25087a5d7d) +- [Android混合编程:WebView实践](https://juejin.im/post/59f17a7051882546d71e91a7) +- [runOnUiThread 、Handler.post、View.post之间的区别](https://blog.csdn.net/dengpeng_/article/details/78804404) +- [理解 Activity.runOnUiThread](https://www.jianshu.com/p/e39449026f21) +- [Android 探究 LayoutInflater setFactory](https://blog.csdn.net/lmj623565791/article/details/51503977) +- [巧用ViewPager 打造不一样的广告轮播切换效果](https://blog.csdn.net/lmj623565791/article/details/51339751) +- [为RecyclerView打造通用Adapter 让RecyclerView更加好用](https://blog.csdn.net/lmj623565791/article/details/51118836) +- [MNCrashMonitor 监听程序崩溃日志,直接页面展示崩溃日志列表](http://www.wanandroid.com/blog/show/2207) +- [『进阶之路』—— 线程池](http://www.wanandroid.com/blog/show/2264) +- [从json文件到炫酷动画-Lottie实现思路和源码分析](https://www.jianshu.com/p/81be1bf9600c) + + +### Gradle相关 + +- [如何理解 Transform API](https://juejin.im/entry/59776f2bf265da6c4741db2b) +- [Android Dex分包之旅](http://yydcdut.com/2016/03/20/split-dex/) +- [gradle简单入门系列](http://www.cnblogs.com/davenkin/p/gradle-learning-1.html) +- [Gradle简单配置](https://mp.weixin.qq.com/s/1UHcYOudViMhpUYeREZzGA) +- [Android 如何编写基于编译时注解的项目](https://blog.csdn.net/lmj623565791/article/details/51931859) + + +### 插件化相关 + +- [滴滴插件化方案 VirtualApk 源码解析](https://blog.csdn.net/lmj623565791/article/details/75000580) + + +### 热修复相关 + +- [Android 热修复 Tinker Gradle Plugin解析](https://blog.csdn.net/lmj623565791/article/details/72667669) +- [Android 热修复 Tinker接入及源码浅析](https://blog.csdn.net/lmj623565791/article/details/54882693) +- [Android 热修复 Tinker 源码分析之DexDiff / DexPatch](https://blog.csdn.net/lmj623565791/article/details/60874334) @@ -84,6 +132,7 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 ### 编译器相关 +- [Android Studio 3.0 新功能解析和旧项目适配](https://mp.weixin.qq.com/s/met0fke7rKumb7Nlb5hxpA) - [Android-studio使用教程1](/AndroidNote/Android编译器相关/AndroidStudio使用教程(第一弹).md) - [Android-studio使用教程2](/AndroidNote/Android编译器相关/AndroidStudio使用教程(第二弹).md) - [Android-studio使用教程3](/AndroidNote/Android编译器相关/AndroidStudio使用教程(第三弹).md) @@ -96,9 +145,20 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 ### 面试题 - - [**Java面试相关**](https://github.com/linsir6/JavaNote) - +- [Android动态加载技术三个关键问题详解](https://blog.tingyun.com/web/article/detail/166) +- [Android组件化方案](http://blog.csdn.net/guiying712/article/details/55213884) +- [Android插件化系列第(二)篇---动态加载技术之apk换肤](https://www.jianshu.com/p/d1c6e67f7889) +- [Android插件化系列第(三)篇---Hook技术之View点击劫持](https://www.jianshu.com/p/dfc6e3989511) +- [Android性能优化之布局优化](https://www.cnblogs.com/hoolay/p/6248514.html) +- [简易断点续传下载器实现](https://www.jianshu.com/p/5b2e22c42467) +- [超详细面试基础题](https://mp.weixin.qq.com/s/YVvV3-RUjbqYo-DYY3E6nA) +- [JVM结构、GC工作机制详解](http://blog.csdn.net/tonytfjing/article/details/44278233) +- [深入探讨类加载器](https://www.ibm.com/developerworks/cn/java/j-lo-classloader/) +- [找到无序数组中最小的K个数](https://www.cnblogs.com/xiaomoxian/archive/2016/02/11/5186762.html) +- [字符编码笔记:ASCII,Unicode和UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) +- [Android 消息机制——你真的了解Handler?](http://blog.csdn.net/qian520ao/article/details/78262289?locationNum=2&fps=1) +- [高并发下线程安全的单例模式](http://blog.csdn.net/cselmu9/article/details/51366946) +- [深入源码解析Android中的Handler,Message,MessageQueue,Looper](http://blog.csdn.net/iispring/article/details/47180325) - [十大基础算法](https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=401441966&idx=1&sn=653fe22c5a7e6c221fbf121124fd18a2&scene=21#wechat_redirect) - [一套比较好的面试题](http://blog.csdn.net/xhmj12/article/details/54730883) - [Android中常见面试题](/AndroidNote/Android面试相关/Android中常见面试题.md) @@ -115,6 +175,13 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [如何终止App的运行](/AndroidNote/Android面试相关/如何终止App的运行.md) - [如何实现Activity切换的动画](/AndroidNote/Android面试相关/如何实现Activity切换的动画.md) + +### 性能优化 + +- [Android开发性能优化总结(一)](http://blog.csdn.net/gs12software/article/details/51173392) +- [Android开发性能优化总结(二)](http://blog.csdn.net/gs12software/article/details/51234454) + + ### 开源框架 - [当下流行开源框架总览](/AndroidNote/Android开源框架相关/Android当下最流行的开源框架总结.md) @@ -124,6 +191,9 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [LinLog](/AndroidNote/Android开源框架相关/一款Android的Log、Toast的库.md) - [Retrofit 2.0 使用教程](http://www.jianshu.com/p/a3e162261ab6) - [retrofit 2.0 源码解析](http://www.jianshu.com/p/0c055ad46b6c) +- [关于 RxJava 背压](https://juejin.im/entry/58e704cbac502e4957b230eb) +- [RxJava 2.0中backpressure(背压)概念的理解](https://blog.csdn.net/jdsjlzx/article/details/52717636) +- [Retrofit2 完全解析 探索与okhttp之间的关系](https://blog.csdn.net/lmj623565791/article/details/51304204) ### 打包 @@ -140,6 +210,28 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 ### Android报错记录 - [Android报错:Manifest-merger-failed-with-multiple-errors,see-logs](https://github.com/linsir6/AndroidNote/blob/master/AndroidNote/Android%E6%8A%A5%E9%94%99%E8%AE%B0%E5%BD%95/Android%E6%8A%A5%E9%94%99-Manifest%20merger%20failed%20with%20multiple%20errors%2C%20see%20logs.md) +- [Android报错-Client not ready yet](/AndroidNote/Android报错记录/Android报错2.md) +- [微信“15。。。。。”背后的故事](https://mp.weixin.qq.com/s/4DBPTN5qoVa976i8uTNAUQ) +- [【Android】当关闭通知消息权限后无法显示系统Toast的解决方案](http://blog.csdn.net/qq_25867141/article/details/52807705) +- [使用Android内置WebView打开TextView中的超链接](http://iluhcm.com/2016/07/06/how-to-open-a-hyperlink-using-app-webview-with-textview/) + + +### Android源码相关 + +- [ Fresco源码解析 - DataSource怎样存储数据](https://blog.csdn.net/feelang/article/details/45420999) + + +---- + +## linux + +- [Android-GitLabCi配置.md](/Linux/Android-GitLabCi配置.md) + +---- + +## Git教程 + +- [Git详细教程](/Git/git详细教程.md) ---- @@ -163,7 +255,7 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [观察者模式](/JavaNote/设计模式相关/观察者模式.md) -### Java相关 +### Java基础相关 - [Java基础知识](/JavaNote/Java相关/Java基础知识.md) - [Java回调原理与实现1](/JavaNote/Java相关/Java回调的原理与实现.md) @@ -177,6 +269,18 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [JVM虚拟机基础知识](/JavaNote/Java相关/JVM虚拟机基础知识.md) - [JVM类加载器](/JavaNote/Java相关/JVM类加载器.md) - [Javase基础知识](https://github.com/francistao/LearningNotes/blob/master/Part2/JavaSE/Java基础知识.md) +- [SparseArray 的使用及实现原理](http://extremej.itscoder.com/sparsearray_source_analyse/) + + +### JavaEE相关 + +- [Spring入门教程](http://how2j.cn/k/spring/spring-ioc-di/87.html) +- [最全面的Spring学习笔记](https://www.cnblogs.com/wangyayun/p/6800902.html) +- [Spring-root入门](/JavaNote/Javaee/Spring-boot入门.md) +- [Spring Boot 配置文件 – 在坑中实践](https://www.bysocket.com/?p=1786) +- [Spring Boot 之 RESRful API 权限控制](https://www.bysocket.com/?p=1080) +- [Spring Boot 整合 Redis 实现缓存操作](https://www.bysocket.com/?p=1756) +- [Spring Boot 官方文档](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/) ---- @@ -236,6 +340,8 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [MySQL导出数据库、表](/WebNote/MySQL相关/Mysql导出数据库、表(有无数据).md) - [Error-ER_TRUNCATED_WRONG_VALUE_FOR_FIELD](/WebNote/MySQL相关/Error--ER_TRUNCATED_WRONG_VALUE_FOR_FIELD.md) - [ERROR-1045-(28000)--Access-denied-for-user-'debian-sys-maint'@'localhost](/WebNote/MySQL相关/ERROR-1045-(28000)--Access-denied-for-user-'debian-sys-maint'@'localho.md) +- [mysql设置远程链接权限](https://www.cnblogs.com/gdsblog/p/7349551.html) +- [关于初次安装mysql8.01遇到的问题解决](https://blog.csdn.net/l569746927/article/details/80025364) ---- @@ -247,15 +353,5 @@ Android-Note同时是博主的一个学习笔记,之前博主尝试着维护 - [浅析RPC协议](/网络协议/浅析RPC协议.md) - [浅析dubbo服务](/网络协议/浅析dubbo服务.md) - [SSH原理与应用](/网络协议/SSH原理与应用.md) - - - - - - - - - - - - +- [理解OAuth 2.0](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) +- [OAuth 2和JWT - 如何设计安全的API?](http://blog.csdn.net/ljinddlj/article/details/53108261)