本节主要介绍下我们常用的 javadoc tag ,虽然内容比较简单,但若正确使用,真的能使我们的代码高大上不少。不仅如此,只要我们按照Javadoc 注释规则,在编码完成后,Javadoc 也能够帮我们从源代码中生成相应的 Html 格式的 API 开发文档。可以点击Oracle规范,我将常用的javadoc tag 根据自己的习惯进行了整理,见下:
所以我们也记第4行的1次执行时间开销为1个时间单元,则 n 次执行开销为 n 个时间单元;同理第3行执行 n 次的时间开销也为 n 个时间单元,所以执行总开销为 (2n + 1) 个时间单元。所以f(N) = 2n+1,根据上文T(N) = c · f(N)到T(N) = O(2n + 1)的大O表示过程知道,我们可以抛弃一些前导的常数和抛弃低阶项,所以T(N) = O(N)。
Swift 5.0 都发布这么久了,而泛型作为该语言的重要特性,还是非常有必要了解一下的。 在 Swift 泛型的运用几乎随处可见,在系统层面,Swift 标准库是通过泛型代码构建出来的,Swift 的数组和字典类型都是泛型集。在主流的轮子里,也是有大量的泛型使用。使用泛型可以提审代码的复用性。 下面就通过实例看看我们的代码怎么写:
/// Creates and returns a new NSTimer object initialized with the specified block object and schedules it on the current run loop in the default mode. /// - parameter: ti The number of seconds between firings of the timer. If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead /// - parameter: repeats If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires. /// - parameter: block The execution body of the timer; the timer itself is passed as the parameter to this block when executed to aid in avoiding cyclical references + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
/* * locationManager:didUpdateLocations: * * Discussion: * Invoked when new locations are available. Required for delivery of * deferred locations. If implemented, updates will * not be delivered to locationManager:didUpdateToLocation:fromLocation: * * locations is an array of CLLocation objects in chronological order. */ - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations API_AVAILABLE(ios(6.0), macos(10.9));
public class BaseBroadReceiverImp extends BroadcastReceiver implements IBroadcast {
//代理广播中绑定成功插件广播
@Override
public void attach(Context context) {
}
//代理广播接收到数据转发给插件中
@Override
public void onReceive(Context context, Intent intent) {
}
}
复制代码
加载插件中 Service
流程图
代码实现
ProxyAcitivy 开启插件中服务
/**
* 加载插件中 启动服务
* @param service
* @return
*/
@Override
public ComponentName startService(Intent service) {
String className = getLoadServiceClassName(service);
Intent intent = new Intent(this,ProxyService.class);
intent.putExtra(Constants.SERVICE_CLASS_NAME,className);
return super.startService(intent);
}
复制代码
ProxyService.java
public class ProxyService extends Service {
private IService iService;
@Override
public IBinder onBind(Intent intent) {
return iService.onBind(intent);
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (iService == null)
init(intent);
return iService.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
iService.onStart(intent,startId);
}
@Override
public boolean onUnbind(Intent intent) {
iService.onUnbind(intent);
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
iService.onDestroy();
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch.
public Condition(Map<String,String> whereCasue) {
//whereArgs 里面的内容存入的 list
ArrayList list = new ArrayList();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("1=1");
//取得所有成员变量的名字
Set<String> keys = whereCasue.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = whereCasue.get(key);
if (value != null){
stringBuffer.append(" and " + key + "=?");
list.add(value);
}
}
public Condition(Map<String,String> whereCasue) {
//whereArgs 里面的内容存入的 list
ArrayList list = new ArrayList();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("1=1");
//取得所有成员变量的名字
Set<String> keys = whereCasue.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = whereCasue.get(key);
if (value != null){
//查询的条件 sql
stringBuffer.append(" and " + key + "=?");
//查询的条件对应的 值
list.add(value);
}
}
public Condition(Map<String,String> whereCasue) {
//whereArgs 里面的内容存入的 list
ArrayList list = new ArrayList();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("1=1");
//取得所有成员变量的名字
Set<String> keys = whereCasue.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = whereCasue.get(key);
if (value != null){
//查询的条件 sql
stringBuffer.append(" and " + key + "=?");
//查询的条件对应的 值
list.add(value);
}
}
/**
* <pre>
* author : devyk on 2019-07-27 18:05
* blog : https://juejin.im/user/3368559355637566/posts
* github : https://github.com/yangkun19921001
* mailbox : yang1001yk@gmail.com
* desc : This is YSubscribe
* </pre>
*/
@Target(ElementType.METHOD) //target 描述此注解在哪里使用
@Retention(RetentionPolicy.RUNTIME) //retention 描述此注解保留的时长 这里是在运行时
public @interface YSubscribe {
YThreadMode threadMode() default YThreadMode.POSTING; //默认是在 post 线程接收数据
String tag() default "";//根据消息来接收事件
}
复制代码
TreadMode 线程模式
/**
* <pre>
* author : devyk on 2019-07-27 18:14
* blog : https://juejin.im/user/3368559355637566/posts
* github : https://github.com/yangkun19921001
* mailbox : yang1001yk@gmail.com
* desc : This is YThreadMode
* </pre>
*/
public enum YThreadMode {
/**
* Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
* implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
* simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
* using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
*/
POSTING,
/**
* On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN,
/**
* On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
* the event will always be queued for delivery. This ensures that the post call is non-blocking.
*/
MAIN_ORDERED,
/**
* On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
* return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
*/
BACKGROUND,
/**
* Subscriber will be called in a separate thread. This is always independent from the posting thread and the
* main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
* use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
* of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
* uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
*/
ASYNC
这个其实很简单,拿到 post 发送的 tag,跟订阅者的 tag 比较下就行了。其实只要了解原理也没有那么难得。
//判断这个方法是否接收事件
if (!TextUtils.isEmpty(tag) && subscribleMethod.getTag().equals(tag) //注解上面的 tag 是否跟发送者的 tag 相同,相同就接收
&& subscribleMethod.getEventType().isAssignableFrom(object.getClass() //判断类型
)
复制代码
总结
最后我们根据开源项目 EventBus 实现了自己 代码传送阵 YEventBus 框架,可以根据 TAG 发送/接收消息。只要了解开源框架原理,根据自己需求改动原有框架或者实现自己的框架都不是太难,加油!
public T startService(Class serviceClass) { try {
...
final T service; try {
Constructorconstructor = serviceClass.getConstructor(Context.class); //实例化对象
service = constructor.newInstance(mContext);
} catch (InvocationTargetException ex) { thrownew RuntimeException("Failed to create service " + name
+ ": service constructor threw an exception", ex);
}
// 代码测试
new Thread("thread-1"){
@Override
public void run() {
ThreadLocal<String> mThread_A = new ThreadLocal();
mThread_A.set("thread-1");
System.out.println("mThread_A :"+mThread_A.get());
}
}.start();
//打印结果
mThread_A :thread-1
复制代码
主线程中存,子线程取
//主线程中存,子线程取
final ThreadLocal<String> mThread_B = new ThreadLocal();
mThread_B.set("thread_B");
new Thread(){
@Override
public void run() {
System.out.println("mThread_B :"+mThread_B.get());
}
}.start();
//打印结果
mThread_B :null
复制代码
主线程存,主线程取
//主线程存,主线程取
ThreadLocal<String> mThread_C = new ThreadLocal();
mThread_C.set("thread_C");
System.out.println("mThread_C :"+mThread_C.get());
针对ListView中造成的IndexOutOfBoundsException,经常是因为外部也持有了Adapter里数据的引用(如在Adapter的构造函数里直接赋值),这时如果外部引用对数据更改了,但没有及时调用notifyDataSetChanged(),则有可能造成Crash,对此我们封装了一个BaseAdapter,数据统一由Adapter自己维护通知, 同时也极大的避免了The content of the adapter has changed but ListView did not receive a notification,这两类Crash目前得到了统一的解决。
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
at android.widget.AbsListView$UpdateBottomFlagTask.isSuperFloatViewServiceRunning(AbsListView.java:7689)
at android.widget.AbsListView$UpdateBottomFlagTask.doInBackground(AbsListView.java:7665)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 4 more
复制代码
/**
* The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
*/
public open class Any {
// kotlin的函数可以没有函数体,其不是Abstract方法,所以子类不必重写。
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
复制代码
public final native void wait(long var1) throws InterruptedException;
public final void wait(long var1, int var3) throws InterruptedException {
if (var1 < 0L) {
throw new IllegalArgumentException("timeout value is negative");
} else if (var3 >= 0 && var3 <= 999999) {
if (var3 > 0) {
++var1;
}
this.wait(var1);
} else {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
}
public final void wait() throws InterruptedException {
this.wait(0L);
}
public synchronize void method(){
method body
}
等同于
public void method(){
this.intrinsicLock.lock();
try{
method body
}
finally{ this.intrinsicLock.unlock();}
}
复制代码
public void addOnclickListener() {
findViewById(R.id.tvnext).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 从哪个界面进行跳转,则以哪个界面打头,enter 结尾
// 例如 MainActivity$Enter
new MainActivity$Enter()
.intentTo(MainActivity.this, count, str, bean);
}
});
}
}
复制代码
9.这是实体bean
public class StuBean implements Serializable{
public StuBean(int id , String name) {
this.id = id;
this.name = name;
}
//学号
public int id;
//姓名
public String name;
}
复制代码
10、在NextActivity接收并打印数据:
public class NextActivity extends AppCompatActivity {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface LogClassMessage {
public int id () default -1;
public String message() default "";
}
复制代码
注:虽然上面的 id 和 message 定义和接口的方法定义很类似,但是在注解中将 id 和 message 称为:int 元素 id , String 元素 message。而且注解元素的类型是有限制的,并不是任何类型都可以,主要包括:基本数据类型(理论上是没有基本类型的包装类型的,但是由于自动封装箱,所以也不会报错)、String 类型、enum 类型、Class 类型、Annotation 类型、以及以上类型的数组,(没有等字,说明目前注解的元素类型只支持上面列出的这几种),否则编译器便会提示错误。
invalid type 'void ' for annotation member // 例如注解类型为void的错误信息