问:
How to efficiently structure a batch queue processing logic flow
I have a class that acts as a controller for websites. These exchanges take orders (think shopping cart). Some of the sites only allow you to have one active order/shoppingcart at a time, while others have no limitation.

My program will have a constant stream of items that need to be ordered/addedtocarts. For sites that can have an unlimited number of concurrent orders, this is not an issue, as I can just start up a new order as an item request comes in, but for sites that are limited, items will build up in a queue waiting to be processed when the current order is finished. The following is the code I have so far, and although admittingly there might be some holes or gotchas that I am not aware of (please point them out if this is the case), I think it is functional and will get the job done.

However, since the processing of an item, or queued items, can only be triggered by the addition of an item the while(Enabled) seems inefficent and wrong to me. I would like to restructure what I have to incorporate this logic, but I am not sure how to with the concurrency limitation in a thread-safe way.

```<code>class OrderItemTask
{
    public OrderStatus Status { get; private set; }
    public OrderItemParams Params { get; }
}

class SiteController
{
    public bool Enabled { get; private set; }

    //set in constructor, or options somewhere
    SemaphoreSlim maxConcurrency = new SemaphoreSlim( 1, 1 );

    Queue<OrderItemTask> itemQueue = new Queue<OrderItemTask>();

    public void Start()
    {
        lock (this)
        {
            Enabled = true;
            Task.Run( () => ProcessOrderWorker() );
        }
    }

    public void Stop()
    {
        lock (this)
        {
            Enabled = false;
        }
    }

    public OrderItemTask OrderItem(OrderItemParams @params)
    {
        var task = new OrderItemTask() { Params = @params};
        QueueItem( task );
        return task;
    }

    void QueueItem(OrderItemTask task)
     {
        lock (itemQueue)
        {
            itemQueue.Enqueue( task );
        }
    }

    async Task ProcessOrderWorker()
    {
        //while true or while SiteController is enabled
        while (Enabled)
        {
            try
            {
                await maxConcurrency.WaitAsync();
                lock (itemQueue)
                {
                    if (itemQueue.Count > 0)
                    {
                        var itemBatch = new List<OrderItemTask>();
                        while (itemQueue.Count > 0)
                            itemBatch.Add( itemQueue.Dequeue() );

                        placeOrderStrategy.PlaceOrder( itemBatch );
                    }
                }
            }
            catch(Exception e)
            { }
            finally
            {
                maxConcurrency.Release();
            }
    }
}
</code>```

答:
If you don't mind blocking synchronously, you could use the powerful (albeit low level) functionality of Monitor Wait/Pulse. You would wait inside the method ProcessOrderWorker, and you would pulse everytime you change the condition that allows more items to be processed. Here is the waiting part:

```<code>void ProcessOrderWorker()
{
    while (true)
    {
        lock (orderQueue) // acquire the lock
        {
            while (!Enabled || orderQueue.Count == 0) // waiting condition
            {
                Monitor.Wait(orderQueue); // here the lock is released
                                          // while waiting for a pulse
            }
        }

        maxConcurrency.Wait();
        lock (orderQueue)
        {
            if (orderQueue.Count > 0)
            {
                var batch = new List<OrderItemTask>();
                while (orderQueue.Count > 0)
                    batch.Add(orderQueue.Dequeue());
                placeOrderStrategy.PlaceOrder(batch);
            }
        }
        maxConcurrency.Release();
    }
}
</code>```

And here are the locations you should pulse:

```<code>public void Start()
{
    lock (orderQueue)
    {
        Enabled = true;
        Monitor.Pulse(orderQueue);
    }
}

public void Stop()
{
    lock (orderQueue)
    {
        Enabled = false;
        Monitor.Pulse(orderQueue);
    }
}

void QueueOrder(OrderItemTask task)
{
    lock (orderQueue)
    {
        orderQueue.Enqueue(task);
        Monitor.Pulse(orderQueue);
    }
}
</code>```

Body:
I have a class that acts as a controller for websites. These exchanges take orders (think shopping cart). Some of the sites only allow you to have one active order/shoppingcart at a time, while others have no limitation.

My program will have a constant stream of items that need to be ordered/addedtocarts. For sites that can have an unlimited number of concurrent orders, this is not an issue, as I can just start up a new order as an item request comes in, but for sites that are limited, items will build up in a queue waiting to be processed when the current order is finished. The following is the code I have so far, and although admittingly there might be some holes or gotchas that I am not aware of (please point them out if this is the case), I think it is functional and will get the job done.

However, since the processing of an item, or queued items, can only be triggered by the addition of an item the while(Enabled) seems inefficent and wrong to me. I would like to restructure what I have to incorporate this logic, but I am not sure how to with the concurrency limitation in a thread-safe way.

class OrderItemTask
{
    public OrderStatus Status { get; private set; }
    public OrderItemParams Params { get; }
}

class SiteController
{
    public bool Enabled { get; private set; }

    //set in constructor, or options somewhere
    SemaphoreSlim maxConcurrency = new SemaphoreSlim( 1, 1 );

    Queue<OrderItemTask> itemQueue = new Queue<OrderItemTask>();

    public void Start()
    {
        lock (this)
        {
            Enabled = true;
            Task.Run( () => ProcessOrderWorker() );
        }
    }

    public void Stop()
    {
        lock (this)
        {
            Enabled = false;
        }
    }

    public OrderItemTask OrderItem(OrderItemParams @params)
    {
        var task = new OrderItemTask() { Params = @params};
        QueueItem( task );
        return task;
    }

    void QueueItem(OrderItemTask task)
     {
        lock (itemQueue)
        {
            itemQueue.Enqueue( task );
        }
    }

    async Task ProcessOrderWorker()
    {
        //while true or while SiteController is enabled
        while (Enabled)
        {
            try
            {
                await maxConcurrency.WaitAsync();
                lock (itemQueue)
                {
                    if (itemQueue.Count > 0)
                    {
                        var itemBatch = new List<OrderItemTask>();
                        while (itemQueue.Count > 0)
                            itemBatch.Add( itemQueue.Dequeue() );

                        placeOrderStrategy.PlaceOrder( itemBatch );
                    }
                }
            }
            catch(Exception e)
            { }
            finally
            {
                maxConcurrency.Release();
            }
    }
}

Body:
If you don't mind blocking synchronously, you could use the powerful (albeit low level) functionality of Monitor Wait/Pulse. You would wait inside the method ProcessOrderWorker, and you would pulse everytime you change the condition that allows more items to be processed. Here is the waiting part:

void ProcessOrderWorker()
{
    while (true)
    {
        lock (orderQueue) // acquire the lock
        {
            while (!Enabled || orderQueue.Count == 0) // waiting condition
            {
                Monitor.Wait(orderQueue); // here the lock is released
                                          // while waiting for a pulse
            }
        }

        maxConcurrency.Wait();
        lock (orderQueue)
        {
            if (orderQueue.Count > 0)
            {
                var batch = new List<OrderItemTask>();
                while (orderQueue.Count > 0)
                    batch.Add(orderQueue.Dequeue());
                placeOrderStrategy.PlaceOrder(batch);
            }
        }
        maxConcurrency.Release();
    }
}

And here are the locations you should pulse:

public void Start()
{
    lock (orderQueue)
    {
        Enabled = true;
        Monitor.Pulse(orderQueue);
    }
}

public void Stop()
{
    lock (orderQueue)
    {
        Enabled = false;
        Monitor.Pulse(orderQueue);
    }
}

void QueueOrder(OrderItemTask task)
{
    lock (orderQueue)
    {
        orderQueue.Enqueue(task);
        Monitor.Pulse(orderQueue);
    }
}


来源: stackexchange

问:
Export Activesheet - values only
I am using this code which works fine but it also copies:

Formulas

Shapes

Macros embedded on Sheet

I am looking for a way to only copy the values of the sheet, whilst retaining their original formatting and then close the newly created workbook as my macro does.

```<code>Sub export_sheet()
Dim sourceWB As Workbook
Dim destWB As Workbook
Dim strSourceSheet As Worksheet
Dim strname As String
Dim path As String

Application.DisplayAlerts = False

path = ThisWorkbook.path & "\"
strname = "test_" & Format(Now, "dd_mmm_yy_hh_mm_ss") & ".xlsx"

Set strSourceSheet = ActiveSheet

ThisWorkbook.Sheets(strSourceSheet).Copy
ActiveWorkbook.SaveAs Filename:=path & strname, FileFormat:=51, CreateBackup:=True
ActiveWorkbook.Close

Application.DisplayAlerts = True
End Sub
</code>```

答:
Something like this should work for you:</p>

```<code>Sub tgr()

    Dim wb As Workbook
    Dim wsCopy As Worksheet
    Dim wsDest As Worksheet
    Dim rFirst As Range
    Dim rLast As Range
    Dim rDest As Range
    Dim sFolderPath As String
    Dim sFileName As String

    Set wb = ThisWorkbook
    Set wsCopy = wb.ActiveSheet
    Set rFirst = wsCopy.Cells.Find("*", wsCopy.Cells(wsCopy.Rows.Count, wsCopy.Columns.Count), xlValues, xlPart, , xlNext)
    Set rLast = wsCopy.Cells.Find("*", wsCopy.Range("A1"), xlValues, xlPart, , xlPrevious)
    sFolderPath = ThisWorkbook.Path & Application.PathSeparator
    sFileName = "test_" & Format(Now, "dd_mmm_yy_hh_mm_ss") & ".xlsx"

    wb.Worksheets.Add.Move  'create new workbook with a blank worksheet
    Set wsDest = ActiveWorkbook.ActiveSheet 'the newly created workbook and sheet will be active because they were just created
    With wsDest
        Set rDest = .Cells(rFirst.Row, rFirst.Column)
        wsCopy.Range(rFirst, rLast).Copy
        rDest.PasteSpecial xlPasteValues
        rDest.PasteSpecial xlPasteFormats
        rDest.PasteSpecial xlPasteColumnWidths
        .Parent.SaveAs sFolderPath & sFileName, xlOpenXMLWorkbook
        .Parent.Close True
    End With

End Sub
</code>```

ContentLicense: CC BY-SA 4.0

Title: Export Activesheet - values only

Body:
I am using this code which works fine but it also copies:

Formulas

Shapes

Macros embedded on Sheet

I am looking for a way to only copy the values of the sheet, whilst retaining their original formatting and then close the newly created workbook as my macro does.

Sub export_sheet()
Dim sourceWB As Workbook
Dim destWB As Workbook
Dim strSourceSheet As Worksheet
Dim strname As String
Dim path As String

Application.DisplayAlerts = False

path = ThisWorkbook.path & "\"
strname = "test_" & Format(Now, "dd_mmm_yy_hh_mm_ss") & ".xlsx"

Set strSourceSheet = ActiveSheet

ThisWorkbook.Sheets(strSourceSheet).Copy
ActiveWorkbook.SaveAs Filename:=path & strname, FileFormat:=51, CreateBackup:=True
ActiveWorkbook.Close

Application.DisplayAlerts = True
End Sub

Body:
Something like this should work for you:

Sub tgr()

    Dim wb As Workbook
    Dim wsCopy As Worksheet
    Dim wsDest As Worksheet
    Dim rFirst As Range
    Dim rLast As Range
    Dim rDest As Range
    Dim sFolderPath As String
    Dim sFileName As String

    Set wb = ThisWorkbook
    Set wsCopy = wb.ActiveSheet
    Set rFirst = wsCopy.Cells.Find("*", wsCopy.Cells(wsCopy.Rows.Count, wsCopy.Columns.Count), xlValues, xlPart, , xlNext)
    Set rLast = wsCopy.Cells.Find("*", wsCopy.Range("A1"), xlValues, xlPart, , xlPrevious)
    sFolderPath = ThisWorkbook.Path & Application.PathSeparator
    sFileName = "test_" & Format(Now, "dd_mmm_yy_hh_mm_ss") & ".xlsx"

    wb.Worksheets.Add.Move  'create new workbook with a blank worksheet
    Set wsDest = ActiveWorkbook.ActiveSheet 'the newly created workbook and sheet will be active because they were just created
    With wsDest
        Set rDest = .Cells(rFirst.Row, rFirst.Column)
        wsCopy.Range(rFirst, rLast).Copy
        rDest.PasteSpecial xlPasteValues
        rDest.PasteSpecial xlPasteFormats
        rDest.PasteSpecial xlPasteColumnWidths
        .Parent.SaveAs sFolderPath & sFileName, xlOpenXMLWorkbook
        .Parent.Close True
    End With

End Sub


问:
How do you use Google tasks API from an Android app?
I'd like to be able to create a custom app that uses Google tasks. How would I go about using Google tasks API from an Android app?

In other words, how do I make an API call and use the response?

I found a tutorial located here that was helpful.

答:
Register at cloud.google.com, enable the Tasks API, and create android authorization credentials

Add to your AndroidManifest.xml:

```<code><uses-permission android:name="android.permission.GET_ACCOUNTS" android:maxSdkVersion="25"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />
</code>```

Add to your build.gradle dependencies:

```<code>implementation 'com.google.apis:google-api-services-tasks:v1-rev58-1.25.0'
implementation 'com.google.api-client:google-api-client-android:1.28.0'
</code>```

Use AccountManager.newChooseAccountIntent to get an account, then:

```<code>String token = AccountManager.get(context)
    .getAuthToken(<android.accounts.Account>, "oauth2:" + TasksScopes.TASKS, new Bundle(), activity, null, null)
    .getResult()
    .getString(AccountManager.KEY_AUTHTOKEN);
GoogleCredential credential = new GoogleCredential().setAccessToken(token);
Tasks service = new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
    .setApplicationName("app name")
    .build();
</code>```

Now you can use the Tasks API, e.g. service.tasklists().list().execute()

ContentLicense: CC BY-SA 3.0

Body:
Register at cloud.google.com, enable the Tasks API, and create android authorization credentials

Add to your AndroidManifest.xml:

<uses-permission android:name="android.permission.GET_ACCOUNTS" android:maxSdkVersion="25"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />

Add to your build.gradle dependencies:

implementation 'com.google.apis:google-api-services-tasks:v1-rev58-1.25.0'
implementation 'com.google.api-client:google-api-client-android:1.28.0'

Use AccountManager.newChooseAccountIntent to get an account, then:

String token = AccountManager.get(context)
    .getAuthToken(<android.accounts.Account>, "oauth2:" + TasksScopes.TASKS, new Bundle(), activity, null, null)
    .getResult()
    .getString(AccountManager.KEY_AUTHTOKEN);
GoogleCredential credential = new GoogleCredential().setAccessToken(token);
Tasks service = new Tasks.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
    .setApplicationName("app name")
    .build();

Now you can use the Tasks API, e.g. service.tasklists().list().execute()


问:
HTML5 input pattern attribute - regex fails
I'm a bit stumped trying to get the following regex to work

```<code><!DOCTYPE html>
<html>
<body>

<form action="/....php">
  Amount: <input type="text" name="amount" pattern="^\$(\d{1,3}(\,\d{3})*|(\d+))(^\.\d{2})?$" title="Please enter as $XXX,XXX no decimal">
  <input type="submit">
</form>

</body>
</html>
</code>```

I tried dropping the leading carrot and trailing dollar too, as I know these are implied, but it still won't validate the the number entered is of the form $XXX,XXX with no decimal. Other regex parsers are working fine and validate the regex should work: https://regex101.com/r/BZKMK4/1

Are there published limitations to the input regex you can use in the pattern attribute?

答:
A simplified regex seems to do the trick: \$\d{1,3}(,\d{1,3})*

No need to specifically negate decimals with the (^\.\d{2})? - just not including anything here will make the field invalid if a decimal is typed since no dots are allowed in the first place. I think this is what was messing with it since JS regexes do not support this feature, and your link specified a PHP style regex, not JS

input:invalid {
  border: 2px solid red;
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><label for="input-amount">Amount</label>
<input type="text"
       id="input-amount"
       pattern="\$\d{1,3}(,\d{1,3})*"
       placeholder="$XXX,XXX"></code>```

Also, a small suggestion from the UI-side of things, there's no need to make people type the $ sign, just put that outside of the input next to it so that it's implied and all people have to worry about are the numbers.

Right now you type $ alone and it's an invalid field, which is not a great experience.  here's my suggested change:

.prefix,
input {
  border: 2px solid #CCC;
  font-size: 14px;
  padding: 1px;
}

.prefix {
  display: inline-block;
  border-right-width: 0;
}

input {
  border-left-width: 0;
}

input:invalid {
  border-color: red;
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><label for="input-amount">Amount</label>
<span class="prefix">$</span><input type="text"
       id="input-amount"
       pattern="\d{1,3}(,\d{1,3})*"
       placeholder="XXX,XXX"></code>```

It's not perfect, but you get the idea.

Body:
I'm a bit stumped trying to get the following regex to work

<!DOCTYPE html>

<form action="/....php">
  Amount: <input type="text" name="amount" pattern="^\$(\d{1,3}(\,\d{3})*|(\d+))(^\.\d{2})?$" title="Please enter as $XXX,XXX no decimal">
  <input type="submit">
</form>

I tried dropping the leading carrot and trailing dollar too, as I know these are implied, but it still won't validate the the number entered is of the form $XXX,XXX with no decimal. Other regex parsers are working fine and validate the regex should work: https://regex101.com/r/BZKMK4/1

Are there published limitations to the input regex you can use in the pattern attribute?

Body:
A simplified regex seems to do the trick: \$\d{1,3}(,\d{1,3})*

No need to specifically negate decimals with the (^\.\d{2})? - just not including anything here will make the field invalid if a decimal is typed since no dots are allowed in the first place. I think this is what was messing with it since JS regexes do not support this feature, and your link specified a PHP style regex, not JS

input:invalid {
  border: 2px solid red;
}

<label for="input-amount">Amount</label>
<input type="text"
       id="input-amount"
       pattern="\$\d{1,3}(,\d{1,3})*"
       placeholder="$XXX,XXX">

Also, a small suggestion from the UI-side of things, there's no need to make people type the $ sign, just put that outside of the input next to it so that it's implied and all people have to worry about are the numbers.

Right now you type $ alone and it's an invalid field, which is not a great experience.  here's my suggested change:

.prefix,
input {
  border: 2px solid #CCC;
  font-size: 14px;
  padding: 1px;
}

.prefix {
  display: inline-block;
  border-right-width: 0;
}

input {
  border-left-width: 0;
}

input:invalid {
  border-color: red;
}

<label for="input-amount">Amount</label>
<span class="prefix">$</span><input type="text"
       id="input-amount"
       pattern="\d{1,3}(,\d{1,3})*"
       placeholder="XXX,XXX">

It's not perfect, but you get the idea.


问:
What will we do after Access?
Microsoft seems hell-bent on deprecating the swiss-army-knife of database tools. What else comes close for facading/file-swapping/cloning/name-your-acronym-connecting arbitrary database servers/spreadsheets/CSV's/flatfiles?

What weird kinds of functionality have you squeezed out of Access? And what else is there to take its place?

答: I doubt Microsoft will kill off Access. With Access 2007's integration with Sharepoint and the rapid growth of SharePoint, Access may in fact have a resurgence as an off-line and reporting tool for SharePoint web sites.</p>

OwnerDisplayName: doofledorfer

ContentLicense: CC BY-SA 2.5

Title: What will we do after Access?

LastEditorDisplayName: le dorfier

OwnerDisplayName: Chris Latta


问:
ADO recordset crashes while evaluating EOF after Excel upgrade
I have an Excel sheet that connects to a Basis database using an ODBC connection. It worked fine when we were running Excel 2010 on Windows Server 2008, but then we upgraded to Excel 2016 on Windows Server 2016, and it doesn't work anymore.

Here's some code that demonstrates the problem:

```<code>Public Sub cnntest()
Dim cnn As ADODB.Connection
Dim sql As String
Dim rs As ADODB.Recordset

Set cnn = New ADODB.Connection
cnn.Open "DSN=redacted;UID=redacted;PWD=redacted;"

sql = "SELECT ITEM FROM IC_ITM_MST WHERE ITEM = '1400-4'"
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseServer
rs.Open sql, cnn

For Each a In rs.Fields
    Debug.Print a.Name
Next a

Debug.Print rs.EOF

rs.Close
cnn.Close
End Sub
</code>```

When I run the above code, it prints the name of the field, so I know that it's connecting to the database and executing the query. However, when it tries to evaluate rs.EOF, it immediately crashes and throws up an error message saying "Microsoft Excel has stopped working." It also does this when I mouse over that part of the code. The exact same code runs fine when I try it on Excel 2010 on Windows Server 2008.

Any idea why it works on the old version of Excel but not the new one? Any ideas on how to fix it?

答:
Four months later, I finally found the answer. I used the Windows event viewer to look at the error logs for the crashes, which said

"Faulting application name: EXCEL.EXE, version 16.0.4266.1001, time
  stamp: 0x55ba1551
  Faulting module name: BBjODBC.dll, version 14.1.1.0, time stamp: 0x5400df87
  Exception code: 0xc0000005

This confirmed that the problem was with the BBj driver. It turns out that the person responsible for updating that driver had updated it on all of the server's hard drives except for the C drive, which was the one that Excel decided to use. As a result, we were stuck using a five year old version of the driver even though our tech people claimed everything was up to date. We're currently waiting for the driver to be updated, so hopefully that will fix the issue.

Body:
I have an Excel sheet that connects to a Basis database using an ODBC connection. It worked fine when we were running Excel 2010 on Windows Server 2008, but then we upgraded to Excel 2016 on Windows Server 2016, and it doesn't work anymore.

Here's some code that demonstrates the problem:

Public Sub cnntest()
Dim cnn As ADODB.Connection
Dim sql As String
Dim rs As ADODB.Recordset

Set cnn = New ADODB.Connection
cnn.Open "DSN=redacted;UID=redacted;PWD=redacted;"

sql = "SELECT ITEM FROM IC_ITM_MST WHERE ITEM = '1400-4'"
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseServer
rs.Open sql, cnn

For Each a In rs.Fields
    Debug.Print a.Name
Next a

Debug.Print rs.EOF

rs.Close
cnn.Close
End Sub

When I run the above code, it prints the name of the field, so I know that it's connecting to the database and executing the query. However, when it tries to evaluate rs.EOF, it immediately crashes and throws up an error message saying "Microsoft Excel has stopped working." It also does this when I mouse over that part of the code. The exact same code runs fine when I try it on Excel 2010 on Windows Server 2008.

Any idea why it works on the old version of Excel but not the new one? Any ideas on how to fix it?


问:
What would be best practice If I am trying to constantly check if a directory exists? JAVA
I have a Java application that creates multiple threads. There is 1 producer thread which reads from a 10gb file, parses that information, creates objects from it and puts them into multiple blocking queues (5 queues).

The rest of the 5 consumer threads read from a blockingqueue (each consumer thread has its own blockingqueue). The consumer threads then each write to an individual file, so 5 files in total get created. It takes around 30min to create all files.

The problem:
The threads are writing to an external mount directory in a linux box. We've experience problems where other linux mounts have gone down and applications crash so I want to prevent that in this application.

What I would like to do is keep checking if the mount (directory) exists before writing to it. Im assuming if the directory goes down it will throw a FileNotFoundException. If that is the case I want it to keep checking if the directory is there for about 10-20min before completely crashing. Because I dont want to have to read the 10gb file again I want the consumer threads to be able to pick up from where they last left off.

What Im not sure would be best practice is:

Is it best to check if the directory exists in the main class before creating the threads? Or check in each consumer thread?

If I keep checking if the directory exists in each consumer thread it seems like repeatable code. I can check in the main class but it takes 30min to create these files. What if in those 30min the mount goes down then if Im only checking in the main class whether the directory exists the application will crash. Or if Im already writing to a directory is it impossible for an external directory to go down? Does it get locked?

thank you

答:
write a method to detect folder exist or not.

call this method before actual writing.

create 5 thread based on 2. Once detect file is not existed, you seems have no choice but rewrite. Of course, you don't need re-read if all your content are in memory already(Big memory).


问:
Ignoring specified items of StringList
I would like to create a program (learning Delphi) that takes 3 different list (from TEdit)

A list of names

A list of names to ignore when searching first list

A list of names to ignore when searching first list

By taking the 3 TEdit and converting the text to TStringList and seperating (I am so far okay with this).

I want to output the first name (or item) of the first list that is neither on 2nd or 3rd list

```<code>procedure TForm1.Button1Click(Sender: TObject);
var
i, j ,k : integer;
begin

  list := TStringList.Create;
  ignoreListFirst := TStringList.Create;
  ignoreListSecond := TStringList.Create;

  list.Delimiter := ',';
  ignoreListFirst.Delimiter := ',';
  ignoreListSecond.Delimiter := ',';

  list.DelimitedText := EditList.Text;
  ignoreListFirst.DelimitedText := EditIgnoreList1.Text;
  ignoreListSecond.DelimitedText := EditIgnoreList2.Text;

  for k := 0 to list.Count - 1 do
  begin
    for i := 0 to ignoreListFirst.Count - 1 do
    begin
      for j := 0 to ignoreListSecond.Count - 1 do
      begin
        if (list[k] <> ignoreListFirst[i]) and (list[k] <> ignoreListSecond[k]) then

        EditResult.Text := list[k];
        break;

      end;
    end;
  end;

  list.Free;
  ignoreListFirst.Free;
  ignoreListSecond.Free;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  EditList.Text := 'Katy,Mary,John,Maggie,Carl';
  EditIgnoreList1.Text := 'Katy,Mary,John';
  EditIgnoreList2.Text := 'John,Carl';
end;

</code>```

Tried swapping the loop order to see if I can pinpoint the problem.

first list in last loop give me no errors and produces the name 'Katy' which is yes, the first on the list but ignored.

Swapped the the first list to be first loop. produces 'Mary' which I guess ignored (which I wanted) Katy but not others from what I can see.

In this example (which does not work) I want it to result 'Maggie' which is not on either ignore lists.

Thanks and sorry if I explained this wrong. Learning Delphi.
Do pinpoint any mistakes.

答:
Basicaly your missing a begin/end.

```<code>for k := 0 to list.Count - 1 do
begin
 for i := 0 to ignoreListFirst.Count - 1 do
 begin
  for j := 0 to ignoreListSecond.Count - 1 do
  begin
    if (list[k] <> ignoreListFirst[i]) and (list[k] <> ignoreListSecond[k]) then
    Begin // here

      EditResult.Text := list[k];
      break;
    End; // here
  end;
 end;
end;
</code>```

In the old code, your loop will alway break.
When not using begin/end only the first line of code following the if, will be dependant of the if condition.
The second line will always execute.

Seconde: break, will only break on the most inner loop. So the two other loop will just continue and therefor another name might be shown...

But instead of using two extra inner loops, you could check if the currect string of the first loop exists inside one of the other loops (see TstringList.IndexOf)

Title: Ignoring specified items of StringList

Body:
I would like to create a program (learning Delphi) that takes 3 different list (from TEdit)

A list of names

A list of names to ignore when searching first list

A list of names to ignore when searching first list

By taking the 3 TEdit and converting the text to TStringList and seperating (I am so far okay with this).

I want to output the first name (or item) of the first list that is neither on 2nd or 3rd list

procedure TForm1.Button1Click(Sender: TObject);
var
i, j ,k : integer;
begin

  list := TStringList.Create;
  ignoreListFirst := TStringList.Create;
  ignoreListSecond := TStringList.Create;

  list.Delimiter := ',';
  ignoreListFirst.Delimiter := ',';
  ignoreListSecond.Delimiter := ',';

  list.DelimitedText := EditList.Text;
  ignoreListFirst.DelimitedText := EditIgnoreList1.Text;
  ignoreListSecond.DelimitedText := EditIgnoreList2.Text;

  for k := 0 to list.Count - 1 do
  begin
    for i := 0 to ignoreListFirst.Count - 1 do
    begin
      for j := 0 to ignoreListSecond.Count - 1 do
      begin
        if (list[k] <> ignoreListFirst[i]) and (list[k] <> ignoreListSecond[k]) then

        EditResult.Text := list[k];
        break;

      end;
    end;
  end;

  list.Free;
  ignoreListFirst.Free;
  ignoreListSecond.Free;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  EditList.Text := 'Katy,Mary,John,Maggie,Carl';
  EditIgnoreList1.Text := 'Katy,Mary,John';
  EditIgnoreList2.Text := 'John,Carl';
end;

Tried swapping the loop order to see if I can pinpoint the problem.

first list in last loop give me no errors and produces the name 'Katy' which is yes, the first on the list but ignored.

Swapped the the first list to be first loop. produces 'Mary' which I guess ignored (which I wanted) Katy but not others from what I can see.

In this example (which does not work) I want it to result 'Maggie' which is not on either ignore lists.

Thanks and sorry if I explained this wrong. Learning Delphi.
Do pinpoint any mistakes.

Body:
Basicaly your missing a begin/end.

for k := 0 to list.Count - 1 do
begin
 for i := 0 to ignoreListFirst.Count - 1 do
 begin
  for j := 0 to ignoreListSecond.Count - 1 do
  begin
    if (list[k] <> ignoreListFirst[i]) and (list[k] <> ignoreListSecond[k]) then
    Begin // here

      EditResult.Text := list[k];
      break;
    End; // here
  end;
 end;
end;

In the old code, your loop will alway break.
When not using begin/end only the first line of code following the if, will be dependant of the if condition.
The second line will always execute.

Seconde: break, will only break on the most inner loop. So the two other loop will just continue and therefor another name might be shown...

But instead of using two extra inner loops, you could check if the currect string of the first loop exists inside one of the other loops (see TstringList.IndexOf)


问:
C# Out of Memory Exception Store File as Byte Array in DataSet
I'm having issues with an "out of memory" exception that I can't reproduce, but a build server that runs unit tests hits every time. Running the unit tests on my machine doesn't cause the exception. The changes made were because the original code was having an odd issue with large PDF's in the passed in stream. If you have an idea of why the original code had issues with the large PDF's or why the new code would cause an "out of memory" exception then let me know.

Original Code:

```<code>// stream is a valid Stream and parentKey is a valid int
// Reset the stream position
stream.Position = 0;
int sequenceNumber = 0;
int StreamReadSize = short.MaxValue;
byte[] buffer = new byte[StreamReadSize];
MemoryStream outStream = null;
try
{
    long previousStreamPosition = 0;
    long DataBlockSize  = 52428800;
    int read;
    while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
        if (outStream == null)
            outStream = new MemoryStream(new byte[System.Math.Min(stream.Length - previousStreamPosition, DataBlockSize)]);

        previousStreamPosition = stream.Position;
        outStream.Write(buffer, 0, read);
        if (outStream.Position <= (DataBlockSize - StreamReadSize) && stream.Position < stream.Length)
            continue;

        var dataRow = dataSet.Tables["table_name"].NewRow();
        dataRow["parent_key"] = parentKey;
        dataRow["key"] = FuncThatReturnsNextAvailableKey();
        dataRow["sequence_number"] = ++sequenceNumber;
        // Reset the position and Zip up the data
        outStream.Position = 0;

        dataRow["data_segment"] = FuncThatZipsAStreamToByteArray(outStream);

        dataSet.Tables["table_name"].Rows.Add(dataRow);

        outStream.Flush();
        outStream.Dispose();
        outStream = null;
    }
}
finally
{
    if (outStream != null)
        outStream.Dispose();
}
</code>```

New Code:

```<code>// stream is a valid Stream and parentKey is a valid int
// Reset the stream position and create the variables needed for saving the file data
stream.Position = 0;
int sequenceNumber = 0;
int bytesRead;
int DataBlockSize = 52428800;
byte[] buffer = new byte[DataBlockSize];
while ((bytesRead = stream.Read(buffer, 0, DataBlockSize)) > 0)
{
    sequenceNumber++;

    // Create and initialize the row
    var dataRow = dataSet.Tables["table_name"].NewRow();
    dataRow["parent_key"] = parentKey;
    dataRow["key"] = FuncThatReturnsNextAvailableKey(); ;
    dataRow["sequence_number"] = sequenceNumber;

    // If the stream reads in less data than the size of the buffer then create an appropriately sized version of the buffer
    // that will only hold the data that was read in
    if (bytesRead != DataBlockSize)
    {
        var shrunkBuffer = new byte[bytesRead];
        Array.Copy(buffer, shrunkBuffer, bytesRead);
        using (var memoryStream = new MemoryStream(shrunkBuffer))
            dataRow["data_segment"] = FuncThatZipsAStreamToByteArray(memoryStream);
    }
    else
    {
        using (var memoryStream = new MemoryStream(buffer))
            dataRow["data_segment"] = FuncThatZipsAStreamToByteArray(memoryStream);
    }

    // Add the finished row
    dataSet.Tables["table_name"].Rows.Add(dataRow);
}
</code>```

答:
Use a class memorytributary

From sources at https://gist.github.com/bittercoder/3588074

```<code>        using (System.IO.FileStream stream = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
        {
            using (MemoryTributary memT = new MemoryTributary())
            {

                memT.ReadFrom(stream, stream.Length);
                return memT.ToArray();
            }
        }
</code>```

Body:
I'm having issues with an "out of memory" exception that I can't reproduce, but a build server that runs unit tests hits every time. Running the unit tests on my machine doesn't cause the exception. The changes made were because the original code was having an odd issue with large PDF's in the passed in stream. If you have an idea of why the original code had issues with the large PDF's or why the new code would cause an "out of memory" exception then let me know.

Original Code:

// stream is a valid Stream and parentKey is a valid int
// Reset the stream position
stream.Position = 0;
int sequenceNumber = 0;
int StreamReadSize = short.MaxValue;
byte[] buffer = new byte[StreamReadSize];
MemoryStream outStream = null;
try
{
    long previousStreamPosition = 0;
    long DataBlockSize  = 52428800;
    int read;
    while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
        if (outStream == null)
            outStream = new MemoryStream(new byte[System.Math.Min(stream.Length - previousStreamPosition, DataBlockSize)]);

        previousStreamPosition = stream.Position;
        outStream.Write(buffer, 0, read);
        if (outStream.Position <= (DataBlockSize - StreamReadSize) && stream.Position < stream.Length)
            continue;

        var dataRow = dataSet.Tables["table_name"].NewRow();
        dataRow["parent_key"] = parentKey;
        dataRow["key"] = FuncThatReturnsNextAvailableKey();
        dataRow["sequence_number"] = ++sequenceNumber;
        // Reset the position and Zip up the data
        outStream.Position = 0;

        dataRow["data_segment"] = FuncThatZipsAStreamToByteArray(outStream);

        dataSet.Tables["table_name"].Rows.Add(dataRow);

        outStream.Flush();
        outStream.Dispose();
        outStream = null;
    }
}
finally
{
    if (outStream != null)
        outStream.Dispose();
}

New Code:

// stream is a valid Stream and parentKey is a valid int
// Reset the stream position and create the variables needed for saving the file data
stream.Position = 0;
int sequenceNumber = 0;
int bytesRead;
int DataBlockSize = 52428800;
byte[] buffer = new byte[DataBlockSize];
while ((bytesRead = stream.Read(buffer, 0, DataBlockSize)) > 0)
{
    sequenceNumber++;

    // Create and initialize the row
    var dataRow = dataSet.Tables["table_name"].NewRow();
    dataRow["parent_key"] = parentKey;
    dataRow["key"] = FuncThatReturnsNextAvailableKey(); ;
    dataRow["sequence_number"] = sequenceNumber;

    // If the stream reads in less data than the size of the buffer then create an appropriately sized version of the buffer
    // that will only hold the data that was read in
    if (bytesRead != DataBlockSize)
    {
        var shrunkBuffer = new byte[bytesRead];
        Array.Copy(buffer, shrunkBuffer, bytesRead);
        using (var memoryStream = new MemoryStream(shrunkBuffer))
            dataRow["data_segment"] = FuncThatZipsAStreamToByteArray(memoryStream);
    }
    else
    {
        using (var memoryStream = new MemoryStream(buffer))
            dataRow["data_segment"] = FuncThatZipsAStreamToByteArray(memoryStream);
    }

    // Add the finished row
    dataSet.Tables["table_name"].Rows.Add(dataRow);
}

Body:
Use a class memorytributary

From sources at https://gist.github.com/bittercoder/3588074

        using (System.IO.FileStream stream = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
        {
            using (MemoryTributary memT = new MemoryTributary())
            {

                memT.ReadFrom(stream, stream.Length);
                return memT.ToArray();
            }
        }


问:
Redux dispatch on requestAnimationFrame taking 250-300ms
I'm having a hard time figuring out why this is the case, but one of the simplest actions in my React app is being dispatched on each animation frame like so:

```<code>  step = () => {
    if (this.player.howler.playing()) {
      this.props.playProgress(this.player.seek())
      window.requestAnimationFrame(this.step)
    }
  }
</code>```

playProgress is an absurdly simple action, but is taking a ludicrous amount of time to execute:

```<code>/**
 * Dispatched when currentlyPlaying's position changes
 *
 * @param  {integer} pos Position to progress to (in seconds)
 *
 * @return {object} An action object with a type of PLAY_PROGRESS
 */
export function playProgress(pos) {
  return {
    type: PLAY_PROGRESS,
    pos,
  }
}
</code>```

And it's reduced like so:

```<code>case PLAY_PROGRESS:
  return state
    .setIn(['currentlyPlaying', 'pos'], action.pos)
</code>```

Why on earth would that take so long to execute?? window.requestAnimationFrame only gives you 16ms before it executes again, so 250ms isn't cutting it... Any help is appreciated!

答: This is an old question, but I believe it was related to an infinite scrolling issue we encountered in Chrome. Here's a rundown: github.com/CassetteRocks/react-infinite-scroller/pull/125</p>

Body:
I'm having a hard time figuring out why this is the case, but one of the simplest actions in my React app is being dispatched on each animation frame like so:

  step = () => {
    if (this.player.howler.playing()) {
      this.props.playProgress(this.player.seek())
      window.requestAnimationFrame(this.step)
    }
  }

playProgress is an absurdly simple action, but is taking a ludicrous amount of time to execute:

/**
 * Dispatched when currentlyPlaying's position changes
 *
 * @param  {integer} pos Position to progress to (in seconds)
 *
 * @return {object} An action object with a type of PLAY_PROGRESS
 */
export function playProgress(pos) {
  return {
    type: PLAY_PROGRESS,
    pos,
  }
}

And it's reduced like so:

case PLAY_PROGRESS:
  return state
    .setIn(['currentlyPlaying', 'pos'], action.pos)

Why on earth would that take so long to execute?? window.requestAnimationFrame only gives you 16ms before it executes again, so 250ms isn't cutting it... Any help is appreciated!


问:
Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper> supportLib=28
This weird logcat messages started when I switched to supportLibrary 28, not happens on 27.1.1. I tried with an empty default project and the result exactly the same.

The problem is easily reproducible,

Create a new project with an empty activity and run on an emulator except API28 emulator. It'll give that error on my API21 emulator:

```<code>Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>
</code>```

My API24 device shows more detailed log:

```<code>2018-11-15 22:00:55.563 9948-9948/? I/art: Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/view/View$OnUnhandledKeyEventListener;
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v4.view.ViewCompat.setBackground(android.view.View, android.graphics.drawable.Drawable) (ViewCompat.java:2341)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.widget.ActionBarContainer.<init>(android.content.Context, android.util.AttributeSet) (ActionBarContainer.java:62)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance0!(java.lang.Object[]) (Constructor.java:-2)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) (Constructor.java:430)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createView(java.lang.String, java.lang.String, android.util.AttributeSet) (LayoutInflater.java:645)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:787)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet) (LayoutInflater.java:727)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflate(org.xmlpull.v1.XmlPullParser, android.view.View, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:858)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflateChildren(org.xmlpull.v1.XmlPullParser, android.view.View, android.util.AttributeSet, boolean) (LayoutInflater.java:821)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) (LayoutInflater.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup, boolean) (LayoutInflater.java:426)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup) (LayoutInflater.java:377)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:607)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void com.example.myapplication.MainActivity.onCreate(android.os.Bundle) (MainActivity.java:11)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6666)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1118)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2732)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2844)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1572)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:110)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.os.Looper.loop() (Looper.java:203)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6364)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:1063)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:924)
2018-11-15 22:00:55.563 9948-9948/? I/art: Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.View$OnUnhandledKeyEventListener" on path: DexPathList[[zip file "/data/app/com.example.myapplication-2/base.apk", zip file "/data/app/com.example.myapplication-2/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_9_
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:56)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:380)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v4.view.ViewCompat.setBackground(android.view.View, android.graphics.drawable.Drawable) (ViewCompat.java:2341)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.widget.ActionBarContainer.<init>(android.content.Context, android.util.AttributeSet) (ActionBarContainer.java:62)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance0!(java.lang.Object[]) (Constructor.java:-2)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) (Constructor.java:430)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createView(java.lang.String, java.lang.String, android.util.AttributeSet) (LayoutInflater.java:645)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:787)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet) (LayoutInflater.java:727)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflate(org.xmlpull.v1.XmlPullParser, android.view.View, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:858)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflateChildren(org.xmlpull.v1.XmlPullParser, android.view.View, android.util.AttributeSet, boolean) (LayoutInflater.java:821)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) (LayoutInflater.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup, boolean) (LayoutInflater.java:426)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup) (LayoutInflater.java:377)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:607)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void com.example.myapplication.MainActivity.onCreate(android.os.Bundle) (MainActivity.java:11)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6666)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1118)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2732)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2844)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1572)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:110)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.os.Looper.loop() (Looper.java:203)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6364)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:1063)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:924)
</code>```

I tried API21 emulator, API24 hardware device and API 28 emulator. Only API28 emulator didn't produce that error.

I googled, searched SO and also reported to android issue tracker I can't find any solution and android team says that is an intended behavior. So I don't know what to do. Should I ignore an error message? Is there anybody have this issue?

build.gradle:

```<code>apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
</code>```

MainActivity.java:

```<code>package com.example.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
</code>```

activity_main.xml:

```<code><?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
</code>```

答:
Well, as it appears that Google isn't interested in fixing it; I've decided to (for now) force using support library 27; at least it fixes the crashes on the few devices that support lib 28 crash on.

The fix that I'm using is adding the following into build.gradle however note, this is for a fix for android; only works for users of com.android.support libraries.

```<code>configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == "com.android.support") {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion "27.+"
            }
        }
    }
}
</code>```

Body:
This weird logcat messages started when I switched to supportLibrary 28, not happens on 27.1.1. I tried with an empty default project and the result exactly the same.

The problem is easily reproducible,

Create a new project with an empty activity and run on an emulator except API28 emulator. It'll give that error on my API21 emulator:

Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>

My API24 device shows more detailed log:

2018-11-15 22:00:55.563 9948-9948/? I/art: Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/view/View$OnUnhandledKeyEventListener;
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v4.view.ViewCompat.setBackground(android.view.View, android.graphics.drawable.Drawable) (ViewCompat.java:2341)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.widget.ActionBarContainer.<init>(android.content.Context, android.util.AttributeSet) (ActionBarContainer.java:62)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance0!(java.lang.Object[]) (Constructor.java:-2)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) (Constructor.java:430)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createView(java.lang.String, java.lang.String, android.util.AttributeSet) (LayoutInflater.java:645)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:787)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet) (LayoutInflater.java:727)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflate(org.xmlpull.v1.XmlPullParser, android.view.View, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:858)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflateChildren(org.xmlpull.v1.XmlPullParser, android.view.View, android.util.AttributeSet, boolean) (LayoutInflater.java:821)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) (LayoutInflater.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup, boolean) (LayoutInflater.java:426)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup) (LayoutInflater.java:377)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:607)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void com.example.myapplication.MainActivity.onCreate(android.os.Bundle) (MainActivity.java:11)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6666)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1118)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2732)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2844)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1572)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:110)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.os.Looper.loop() (Looper.java:203)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6364)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:1063)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:924)
2018-11-15 22:00:55.563 9948-9948/? I/art: Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.View$OnUnhandledKeyEventListener" on path: DexPathList[[zip file "/data/app/com.example.myapplication-2/base.apk", zip file "/data/app/com.example.myapplication-2/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.myapplication-2/split_lib_slice_9_
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:56)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:380)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v4.view.ViewCompat.setBackground(android.view.View, android.graphics.drawable.Drawable) (ViewCompat.java:2341)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.widget.ActionBarContainer.<init>(android.content.Context, android.util.AttributeSet) (ActionBarContainer.java:62)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance0!(java.lang.Object[]) (Constructor.java:-2)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) (Constructor.java:430)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createView(java.lang.String, java.lang.String, android.util.AttributeSet) (LayoutInflater.java:645)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:787)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.createViewFromTag(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet) (LayoutInflater.java:727)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflate(org.xmlpull.v1.XmlPullParser, android.view.View, android.content.Context, android.util.AttributeSet, boolean) (LayoutInflater.java:858)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.view.LayoutInflater.rInflateChildren(org.xmlpull.v1.XmlPullParser, android.view.View, android.util.AttributeSet, boolean) (LayoutInflater.java:821)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) (LayoutInflater.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup, boolean) (LayoutInflater.java:426)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.View android.view.LayoutInflater.inflate(int, android.view.ViewGroup) (LayoutInflater.java:377)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:607)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
2018-11-15 22:00:55.563 9948-9948/? I/art:     at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void com.example.myapplication.MainActivity.onCreate(android.os.Bundle) (MainActivity.java:11)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6666)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1118)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2732)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2844)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1572)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:110)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.os.Looper.loop() (Looper.java:203)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6364)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:1063)
2018-11-15 22:00:55.564 9948-9948/? I/art:     at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:924)

I tried API21 emulator, API24 hardware device and API 28 emulator. Only API28 emulator didn't produce that error.

I googled, searched SO and also reported to android issue tracker I can't find any solution and android team says that is an intended behavior. So I don't know what to do. Should I ignore an error message? Is there anybody have this issue?

build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

MainActivity.java:

package com.example.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Body:
Well, as it appears that Google isn't interested in fixing it; I've decided to (for now) force using support library 27; at least it fixes the crashes on the few devices that support lib 28 crash on.

The fix that I'm using is adding the following into build.gradle however note, this is for a fix for android; only works for users of com.android.support libraries.

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == "com.android.support") {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion "27.+"
            }
        }
    }
}


问:
How to show an empty view when using PagedListAdapter with LiveData?
Usually on a RecyclerView I show an empty view when there are no items on the RecyclerView and since I control all updates to the RecyclerView via the notify methods then that is pretty simple but with PagedListAdapter updates just seem to happen on the background, so how can I hide or show my empty view?

For example, if I call deleteItem() on my Room DB, the PagedListAdapter will be updated on its own without me calling notifyItemDeleted but if it was the last item on the list, how does my code know to show the empty view? I could query the DB each time an action happens for the count but that seems wasteful. Is there a better way?

答:
As mentioned in the comment, you can test emptiness of the list in the same LiveData observer you use for .submitList().

Java Example:

I am assuming you are following something similar to this snippet found in the PagedListAdapter document. I am simply adding emptiness check to that sample code.

```<code>class MyActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        RecyclerView recyclerView = findViewById(R.id.user_list);
        UserAdapter<User> adapter = new UserAdapter();
        viewModel.usersList.observe(this, pagedList -> {
            // Check if the list is empty
            updateView(pagedList.size());
            adapter.submitList(pagedList));

            pagedList.addWeakCallback(null, new PagedList.Callback() {

                @Override
                public void onChanged(int position, int count) {
                    updateView(pagedList.size())
                    // updateView(adapter.getItemCount())
                }

                ...
            }
        }
        recyclerView.setAdapter(adapter);
    }

    private void updateView(int itemCount) {
        if (itemCount > 0) {
            // The list is not empty. Show the recycler view.
            recyclerView.setVisibility(View.VISIBLE);
            emptyView.setVisibility(View.GONE);
        } else {
            // The list is empty. Show the empty list view
            recyclerView.setVisibility(View.GONE);
            emptyView.setVisibility(View.VISIBLE);
        }

    }
}
</code>```

Kotlin Example:

The above Java example is actually just a Java translation of Kotlin example I found in this Android Paging codelab.

Body:
As mentioned in the comment, you can test emptiness of the list in the same LiveData observer you use for .submitList().

Java Example:

I am assuming you are following something similar to this snippet found in the PagedListAdapter document. I am simply adding emptiness check to that sample code.

class MyActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        RecyclerView recyclerView = findViewById(R.id.user_list);
        UserAdapter<User> adapter = new UserAdapter();
        viewModel.usersList.observe(this, pagedList -> {
            // Check if the list is empty
            updateView(pagedList.size());
            adapter.submitList(pagedList));

            pagedList.addWeakCallback(null, new PagedList.Callback() {

                @Override
                public void onChanged(int position, int count) {
                    updateView(pagedList.size())
                    // updateView(adapter.getItemCount())
                }

                ...
            }
        }
        recyclerView.setAdapter(adapter);
    }

    private void updateView(int itemCount) {
        if (itemCount > 0) {
            // The list is not empty. Show the recycler view.
            recyclerView.setVisibility(View.VISIBLE);
            emptyView.setVisibility(View.GONE);
        } else {
            // The list is empty. Show the empty list view
            recyclerView.setVisibility(View.GONE);
            emptyView.setVisibility(View.VISIBLE);
        }

    }
}

Kotlin Example:

The above Java example is actually just a Java translation of Kotlin example I found in this Android Paging codelab.


问:
How can I revert this command "git status --porcelain | grep '^??' | cut -c4- >> .gitignore"?
Intially to ignore the untracked files permanently I ran a

```<code>git status --porcelain | grep '^??' | cut -c4- >> .gitignore
</code>```

command. Now when I add a new file in my code, it is not showing in git status.

答:
You have overwritten your .gitignore with the data of your changed files. A git status --porcelain output has no meaning in the syntax of the .gitignore.

You need to recover your .gitignore. The simplest way is - assuming you had no local, not committed change to it - is

```<code>git checkout -- .gitignore
</code>```

Body:
Intially to ignore the untracked files permanently I ran a

git status --porcelain | grep '^??' | cut -c4- >> .gitignore

command. Now when I add a new file in my code, it is not showing in git status.

Body:
You have overwritten your .gitignore with the data of your changed files. A git status --porcelain output has no meaning in the syntax of the .gitignore.

You need to recover your .gitignore. The simplest way is - assuming you had no local, not committed change to it - is

git checkout -- .gitignore


问:
How can I get a string literal (character array?) from a value in javascript?
I am using Google App Script to grab text from a Google Sheet, then upload it to Firebase. JSON encoding of any data from the sheet is adding extra escape characters that I don't need. I am trying to figure out a way to prevent this because it does not add unnecessary escape characters if I use a string literal.

This function below is what I call to sync data from the sheet to firebase. I initialize an object from the gathered data, and then I send it off to the Firebase database for updating.

If I keep it as it is, the string at data[i][1] eventually has escape characters automatically added in. If I replace data[i][1] with any literal string (i.e. "Testing\n\n-Caleb") the value of that key keeps its entire string un-modified.

```<code>// Gets the data from the sheet and puts it in an object.
// It is called by running the sync button in the sheet.
function sync() {
  // get the spreadsheet data
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("main");
  var [rows, columns] = [sheet.getLastRow(), sheet.getLastColumn()];
  var data = sheet.getSheetValues(1, 1, rows, columns);

  // put the data in an object
  testAlert(data[1][1]);   // prints "Testing
                           //
                           //         -Caleb"                   (OK)
  var obj = {}
  for (var i = 1; i < data.length; i++) {
    obj[i - 1] = {
      Happy: data[i][0],
      Meh: data[i][1],             // THE VALUE OF TOPIC
    //Meh: "Testing\n\n-Caleb"     // THE LITERAL STRING OPTION
      Down: data[i][2],
      Sad: data[i][3],
      Angry: data[i][4]
    }
  }
  testAlert(obj[0].Meh).   // prints "Testing
                           //
                           //         -Caleb"                   (OK)

  // convert to unused JSON object to print to the window what is happening
  var jsonOBJ = JSON.stringify(obj[0].Meh)
  testAlert(jsonOBJ).      // prints "\"Testing\\n\\n-Caleb\"" (ERROR)

  updateFirebaseData(obj, "emotions");
}
</code>```

This is the function called at the end:

```<code>// updates the firebase database with specified data and location
function updateFirebaseData(obj, loc) {
  var firebaseUrl = "https://gem-0-2.firebaseio.com/";
  var secret = "FirebaseSecret";
  var base = FirebaseApp.getDatabaseByUrl(firebaseUrl, secret);
  var result = base.updateData(loc, obj);
}
</code>```

The data at data[i][1] is "Testing\n\n-Caleb" and by the time it reaches the database, it is "\"Testing\n\n-Caleb\"", and so escaped characters that I intentionally place, like end-lines, aren't translated properly. However, if I put "Testing\n\n-Caleb" as the value for "Meh", then it stays as-is.

答:
sheet.getSheetValues() is returning a string that's already encoded as JSON. If you want to store the actual string, you need to decode it with JSON.parse().

```<code>  for (var i = 1; i < data.length; i++) {
    obj[i - 1] = {
      Happy: data[i][0],
      Meh: JSON.parse(data[i][1]),             // THE VALUE OF TOPIC
      Down: data[i][2],
      Sad: data[i][3],
      Angry: data[i][4]
    }
</code>```

Body:
I am using Google App Script to grab text from a Google Sheet, then upload it to Firebase. JSON encoding of any data from the sheet is adding extra escape characters that I don't need. I am trying to figure out a way to prevent this because it does not add unnecessary escape characters if I use a string literal.

This function below is what I call to sync data from the sheet to firebase. I initialize an object from the gathered data, and then I send it off to the Firebase database for updating.

If I keep it as it is, the string at data[i][1] eventually has escape characters automatically added in. If I replace data[i][1] with any literal string (i.e. "Testing\n\n-Caleb") the value of that key keeps its entire string un-modified.

// Gets the data from the sheet and puts it in an object.
// It is called by running the sync button in the sheet.
function sync() {
  // get the spreadsheet data
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("main");
  var [rows, columns] = [sheet.getLastRow(), sheet.getLastColumn()];
  var data = sheet.getSheetValues(1, 1, rows, columns);

  // put the data in an object
  testAlert(data[1][1]);   // prints "Testing
                           //
                           //         -Caleb"                   (OK)
  var obj = {}
  for (var i = 1; i < data.length; i++) {
    obj[i - 1] = {
      Happy: data[i][0],
      Meh: data[i][1],             // THE VALUE OF TOPIC
    //Meh: "Testing\n\n-Caleb"     // THE LITERAL STRING OPTION
      Down: data[i][2],
      Sad: data[i][3],
      Angry: data[i][4]
    }
  }
  testAlert(obj[0].Meh).   // prints "Testing
                           //
                           //         -Caleb"                   (OK)

  // convert to unused JSON object to print to the window what is happening
  var jsonOBJ = JSON.stringify(obj[0].Meh)
  testAlert(jsonOBJ).      // prints "\"Testing\\n\\n-Caleb\"" (ERROR)

  updateFirebaseData(obj, "emotions");
}

This is the function called at the end:

// updates the firebase database with specified data and location
function updateFirebaseData(obj, loc) {
  var firebaseUrl = "https://gem-0-2.firebaseio.com/";
  var secret = "FirebaseSecret";
  var base = FirebaseApp.getDatabaseByUrl(firebaseUrl, secret);
  var result = base.updateData(loc, obj);
}

The data at data[i][1] is "Testing\n\n-Caleb" and by the time it reaches the database, it is "\"Testing\n\n-Caleb\"", and so escaped characters that I intentionally place, like end-lines, aren't translated properly. However, if I put "Testing\n\n-Caleb" as the value for "Meh", then it stays as-is.

Body:
sheet.getSheetValues() is returning a string that's already encoded as JSON. If you want to store the actual string, you need to decode it with JSON.parse().

  for (var i = 1; i < data.length; i++) {
    obj[i - 1] = {
      Happy: data[i][0],
      Meh: JSON.parse(data[i][1]),             // THE VALUE OF TOPIC
      Down: data[i][2],
      Sad: data[i][3],
      Angry: data[i][4]
    }


问:
Problems with circle to circle collision in clusters
I am making a game, similar to agar.io, and am working on adding collision to my gameobjects, which all consist of circles. The collision system works pretty well sometimes, but as soon as I get more than 2 in a tight cluster things start getting very glitchy and jittery. Even occasionally when only having 2 they jump arround sporadically. I think the fact that I am trying to move these cells while checking for the collisions is part of the problem, but thats hard to test for.

I am working with Sockets.io, Node.js, and a drawing library called P5.js. The collision is all being handled server side, but the movement of the cells is handled client side.

On the server, the collision code

```<code>socket.on('update', msgUpdate)
    function msgUpdate(data){
    var cell

    //finds the cell that we want to update
    for(var si=0; si<cells.length; si++){
        if(socket.id + data.di == cells[si].id){
        cell = cells[si];
        }
    }

    //the collision system
    for(var i = cells.length-1; i>=0; i--){
        var dx = cell.x - cells[i].x;
        var dy = cell.y - cells[i].y;
        var distance = Math.sqrt(dx * dx + dy * dy);
        if(distance < cell.r + cells[i].r && cell.r > cells[i].r){
            var unitX = dx/distance;
            var unitY = dy/distance;

            cell.x = cells[i].x + (cell.r + cells[i].r + 1) * unitX;
            cell.y = cells[i].y + (cell.r + cells[i].r + 1) * unitY;
       }
    }
}

</code>```

On the client

```<code>//myCells is an array of Cell objects, all of which you control
for(var i = myCells.length-1; i>=0; i--){
    myCells[i].move();
    var data = {
        x: myCells[i].pos.x,
        y: myCells[i].pos.y,
        r: myCells[i].r,
        c: myCells[i].c,
        di: myCells[i].id
    }
    socket.emit('update', data);
}
</code>```

And the client side movement script, within the Cell object

```<code>
this.move = function(){
    for(var ii = myCells.length-1; ii>=0; ii--){
        var velocity = createVector(mouseX - width/2 + (myCells[ii].pos.x - this.pos.x), mouseY-height/2 + (myCells[ii].pos.y - this.pos.y));
    }
    var sub = this.r * 0.03;
    velocity.setMag((6 - sub));
    this.pos.add(velocity);
}
</code>```

答:
The client could send inputs(mouse position) and all the tracking is handled by the server and the server answer back the positions of where to draw things.  That way you broadcast an update when all the cells are check for collision and it can be at periodically (like frame rate).  The client only task is to render the given data and tell the position of the mouse.

Also, I would recommend avoiding creating new variables in loops.

```<code>    let dx;
    let dy;
    let distance;
    let i;
    let unitX;
    let unitY;
    //the collision system
    for(i = cells.length-1; i>=0; i--){
        dx = cell.x - cells[i].x
        dy = cell.y - cells[i].y;
        distance = Math.sqrt(dx * dx + dy * dy);
        if(distance < cell.r + cells[i].r && cell.r > cells[i].r){
            unitX = dx/distance;
            unitY = dy/distance;

            cell.x = cells[i].x + (cell.r + cells[i].r + 1) * unitX;
            cell.y = cells[i].y + (cell.r + cells[i].r + 1) * unitY;
       }
    }
</code>```

Reusing variable help the performance of the code.  Garbage collection and instantiation can be costly in loops.

Another tip to accelerate the check could be to check first if comparing the x and y distance with the radius.  This avoids doing square root that can be also costly and not precise.

example if

```<code>c1.y=0
c1.x=0
c1.r=10

c2.y=0
c2.x=1000
c2.r=10

then they do not intersect

if(cell.r + cells[i].r < dx || cell.r + cells[i].r < dy ){
 //No intersection
}
</code>```

Body:
I am making a game, similar to agar.io, and am working on adding collision to my gameobjects, which all consist of circles. The collision system works pretty well sometimes, but as soon as I get more than 2 in a tight cluster things start getting very glitchy and jittery. Even occasionally when only having 2 they jump arround sporadically. I think the fact that I am trying to move these cells while checking for the collisions is part of the problem, but thats hard to test for.

I am working with Sockets.io, Node.js, and a drawing library called P5.js. The collision is all being handled server side, but the movement of the cells is handled client side.

On the server, the collision code

socket.on('update', msgUpdate)
    function msgUpdate(data){
    var cell

    //finds the cell that we want to update
    for(var si=0; si<cells.length; si++){
        if(socket.id + data.di == cells[si].id){
        cell = cells[si];
        }
    }

    //the collision system
    for(var i = cells.length-1; i>=0; i--){
        var dx = cell.x - cells[i].x;
        var dy = cell.y - cells[i].y;
        var distance = Math.sqrt(dx * dx + dy * dy);
        if(distance < cell.r + cells[i].r && cell.r > cells[i].r){
            var unitX = dx/distance;
            var unitY = dy/distance;

            cell.x = cells[i].x + (cell.r + cells[i].r + 1) * unitX;
            cell.y = cells[i].y + (cell.r + cells[i].r + 1) * unitY;
       }
    }
}

On the client

//myCells is an array of Cell objects, all of which you control
for(var i = myCells.length-1; i>=0; i--){
    myCells[i].move();
    var data = {
        x: myCells[i].pos.x,
        y: myCells[i].pos.y,
        r: myCells[i].r,
        c: myCells[i].c,
        di: myCells[i].id
    }
    socket.emit('update', data);
}

And the client side movement script, within the Cell object

this.move = function(){
    for(var ii = myCells.length-1; ii>=0; ii--){
        var velocity = createVector(mouseX - width/2 + (myCells[ii].pos.x - this.pos.x), mouseY-height/2 + (myCells[ii].pos.y - this.pos.y));
    }
    var sub = this.r * 0.03;
    velocity.setMag((6 - sub));
    this.pos.add(velocity);
}

Body:
The client could send inputs(mouse position) and all the tracking is handled by the server and the server answer back the positions of where to draw things.  That way you broadcast an update when all the cells are check for collision and it can be at periodically (like frame rate).  The client only task is to render the given data and tell the position of the mouse.

Also, I would recommend avoiding creating new variables in loops.

    let dx;
    let dy;
    let distance;
    let i;
    let unitX;
    let unitY;
    //the collision system
    for(i = cells.length-1; i>=0; i--){
        dx = cell.x - cells[i].x
        dy = cell.y - cells[i].y;
        distance = Math.sqrt(dx * dx + dy * dy);
        if(distance < cell.r + cells[i].r && cell.r > cells[i].r){
            unitX = dx/distance;
            unitY = dy/distance;

            cell.x = cells[i].x + (cell.r + cells[i].r + 1) * unitX;
            cell.y = cells[i].y + (cell.r + cells[i].r + 1) * unitY;
       }
    }

Reusing variable help the performance of the code.  Garbage collection and instantiation can be costly in loops.

Another tip to accelerate the check could be to check first if comparing the x and y distance with the radius.  This avoids doing square root that can be also costly and not precise.

example if

c1.y=0
c1.x=0
c1.r=10

c2.y=0
c2.x=1000
c2.r=10

then they do not intersect

if(cell.r + cells[i].r < dx || cell.r + cells[i].r < dy ){
 //No intersection
}


问:
Javascript code not running, how can I fix?
My code is supposed to add 1 to the counter when I hit space and then change the magnifying glass to tilt the other direction. It just doesn't do anything when I hit space.

I've fixed every problem I saw, nothing worked. Maybe somebody else knows, I'm really not that good at js.

```<code>var hits = 0;
var hitElement = document.querySelector( '.hits' );
document.body.onkeydown = function(e) {
  if( e.keyCode == 32 ) {
    addHit();
  }
}

var mag = "";
var hitElement2 = document.querySelector( '.mag' );
document.body.onkeydown = function(f) {
  if( f.keyCode == 32 ) {
    if( mag.text == "") {
        mag = "";
        renderMag();
    else
        mag = "";
        renderMag();
    }
  }
}

var addHit = function() {
  hits++;
  renderHits();
}

var renderMag = function() {
  hitElement2.innerHTML = mag;
}

var renderHits = function() {
  hitElement.innerHTML = hits;
}

var resetHits = function() {
  hits = 0;
  renderHits();
}
</code>```

Console says absolutely nothing too.

答:
I fixed a few things and changed a few things that didn't need fixing. The three key points:

As mentioned, if a bit harshly, by Randy, your second event listener was overwriting your first, so hits was not increasing

The brackets in the if statement of your second event listener had some issues

Though checking the mag icon might work, using icons in the code feels like asking for something to break, so I changed that to a modulo check, to see whether hits was odd or even.

You were on the right track. When you have issues like this in the future, try putting console log statements in various places in the code. Make predictions about what variables should have what values at what points, then check if they do, then, if they don't, try to figure out why not.

var hits = 0;
var hitElement = document.querySelector('.hits');

var mag = "";
var hitElement2 = document.querySelector('.mag');
document.body.onkeydown = function(f) {
  if (f.key == ' ') {
    addHit();
    if (hits % 2 === 0) {
      mag = "";
      renderMag();
    } else {
      mag = "";
      renderMag();
    }
  }
}

function addHit() {
  hits++;
  renderHits();
}

function renderMag() {
  hitElement2.innerHTML = mag;
}

function renderHits() {
  hitElement.innerHTML = hits;
}

function resetHits() {
  hits = 0;
  renderHits();
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><div class="hits"></div>
<div class="mag"></div></code>```

Body:
My code is supposed to add 1 to the counter when I hit space and then change the magnifying glass to tilt the other direction. It just doesn't do anything when I hit space.

I've fixed every problem I saw, nothing worked. Maybe somebody else knows, I'm really not that good at js.

var hits = 0;
var hitElement = document.querySelector( '.hits' );
document.body.onkeydown = function(e) {
  if( e.keyCode == 32 ) {
    addHit();
  }
}

var mag = "";
var hitElement2 = document.querySelector( '.mag' );
document.body.onkeydown = function(f) {
  if( f.keyCode == 32 ) {
    if( mag.text == "") {
        mag = "";
        renderMag();
    else
        mag = "";
        renderMag();
    }
  }
}

var addHit = function() {
  hits++;
  renderHits();
}

var renderMag = function() {
  hitElement2.innerHTML = mag;
}

var renderHits = function() {
  hitElement.innerHTML = hits;
}

var resetHits = function() {
  hits = 0;
  renderHits();
}

Console says absolutely nothing too.

Body:
I fixed a few things and changed a few things that didn't need fixing. The three key points:

As mentioned, if a bit harshly, by Randy, your second event listener was overwriting your first, so hits was not increasing

The brackets in the if statement of your second event listener had some issues

Though checking the mag icon might work, using icons in the code feels like asking for something to break, so I changed that to a modulo check, to see whether hits was odd or even.

You were on the right track. When you have issues like this in the future, try putting console log statements in various places in the code. Make predictions about what variables should have what values at what points, then check if they do, then, if they don't, try to figure out why not.

var hits = 0;
var hitElement = document.querySelector('.hits');

var mag = "";
var hitElement2 = document.querySelector('.mag');
document.body.onkeydown = function(f) {
  if (f.key == ' ') {
    addHit();
    if (hits % 2 === 0) {
      mag = "";
      renderMag();
    } else {
      mag = "";
      renderMag();
    }
  }
}

function addHit() {
  hits++;
  renderHits();
}

function renderMag() {
  hitElement2.innerHTML = mag;
}

function renderHits() {
  hitElement.innerHTML = hits;
}

function resetHits() {
  hits = 0;
  renderHits();
}

<div class="hits"></div>
<div class="mag"></div>